web-dev-qa-db-ja.com

現在のDOMを抽出し、スタイルをそのままにして文字列として出力します

DOMをそのまま使用して、文字列に変換できるようにしたいと思います。インスペクターを開き、特定の要素のmargin-leftプロパティに変更を加えたとしましょう。その変更は私の文字列に反映されるはずです。

関数は、要素に現在適用されているすべてのスタイル(デフォルト値を除く)を適切に取得し、その要素のインラインスタイルに含める必要があります。

不十分であることが証明された「解決策」を書きました。 webkitのgetMatchedCSSRules関数は非常に細心の注意を払っており、なぜ機能するのか、他のときに機能しないのかを判断できませんでした。したがって、100%動作しない限り、この関数の使用は避けたいと思います。同様に、getComputedStyle関数には独自の問題があります。インスペクターを使用してこのページの#footer要素を_7px solid red_ではなく_7px solid black_に変更すると、変更はコンソールでgetComputedStyle(document.getElementById('footer')).cssTextを実行したときに反映されますが、また、インスペクターを使用するユーザーまたはページ上のスタイルシートによって変更されていない、継承されたプロパティのホストを提供します。

Webkitで動作するソリューションを探しています。現時点では、ブラウザ間の互換性は問題ではありません。

ありがとうございました!

50
D-Nice

これは解決策になると思います(ほぼ1日かかりました!)。

デフォルト値を除くすべての外部スタイルが「スタイル」属性に含まれる要素のDOMを表す文字列を返し、その要素を永続的に変更しません。

例:console.log(document.body.serializeWithStyles());

このコードは、Web Inspectorコマンドラインまたはbody要素のscriptタグから読み込むことができますが、document.bodyの存在が必要なため、head要素には読み込むことができません。

デスクトップのSafari 5でテストしました(モバイルバージョンはありません)。

それはこのように動作します:

DOMの各要素に対して:
1)インラインスタイルを表すstyle.cssTextプロパティの値を配列にキャッシュします。
2)要素でgetComputedStyleを呼び出します。
3)この要素のタグ名に対応するcssデフォルト値ルックアップテーブルがあるかどうかを確認します。
4)そうでない場合は構築します。
5)結果を反復処理し、ルックアップテーブルを使用してどの値がデフォルトではないかを見つけます。
6)これらのデフォルト以外のスタイル値を要素に適用します。
その後、outerHTMLを結果として保存します。
各要素について、キャッシュからインラインスタイルを復元します。
以前に保存した結果を返します。

コード:

Element.prototype.serializeWithStyles = (function () {  

    // Mapping between tag names and css default values lookup tables. This allows to exclude default values in the result.
    var defaultStylesByTagName = {};

    // Styles inherited from style sheets will not be rendered for elements with these tag names
    var noStyleTags = {"BASE":true,"HEAD":true,"HTML":true,"META":true,"NOFRAME":true,"NOSCRIPT":true,"PARAM":true,"SCRIPT":true,"STYLE":true,"TITLE":true};

    // This list determines which css default values lookup tables are precomputed at load time
    // Lookup tables for other tag names will be automatically built at runtime if needed
    var tagNames = ["A","ABBR","ADDRESS","AREA","ARTICLE","ASIDE","AUDIO","B","BASE","BDI","BDO","BLOCKQUOTE","BODY","BR","BUTTON","CANVAS","CAPTION","CENTER","CITE","CODE","COL","COLGROUP","COMMAND","DATALIST","DD","DEL","DETAILS","DFN","DIV","DL","DT","EM","EMBED","FIELDSET","FIGCAPTION","FIGURE","FONT","FOOTER","FORM","H1","H2","H3","H4","H5","H6","HEAD","HEADER","HGROUP","HR","HTML","I","IFRAME","IMG","INPUT","INS","KBD","KEYGEN","LABEL","LEGEND","LI","LINK","MAP","MARK","MATH","MENU","META","METER","NAV","NOBR","NOSCRIPT","OBJECT","OL","OPTION","OPTGROUP","OUTPUT","P","PARAM","PRE","PROGRESS","Q","RP","RT","Ruby","S","SAMP","SCRIPT","SECTION","SELECT","SMALL","SOURCE","SPAN","STRONG","STYLE","SUB","SUMMARY","SUP","SVG","TABLE","TBODY","TD","TEXTAREA","TFOOT","TH","THEAD","TIME","TITLE","TR","TRACK","U","UL","VAR","VIDEO","WBR"];

    // Precompute the lookup tables.
    for (var i = 0; i < tagNames.length; i++) {
        if(!noStyleTags[tagNames[i]]) {
            defaultStylesByTagName[tagNames[i]] = computeDefaultStyleByTagName(tagNames[i]);
        }
    }

    function computeDefaultStyleByTagName(tagName) {
        var defaultStyle = {};
        var element = document.body.appendChild(document.createElement(tagName));
        var computedStyle = getComputedStyle(element);
        for (var i = 0; i < computedStyle.length; i++) {
            defaultStyle[computedStyle[i]] = computedStyle[computedStyle[i]];
        }
        document.body.removeChild(element); 
        return defaultStyle;
    }

    function getDefaultStyleByTagName(tagName) {
        tagName = tagName.toUpperCase();
        if (!defaultStylesByTagName[tagName]) {
            defaultStylesByTagName[tagName] = computeDefaultStyleByTagName(tagName);
        }
        return defaultStylesByTagName[tagName];
    }

    return function serializeWithStyles() {
        if (this.nodeType !== Node.ELEMENT_NODE) { throw new TypeError(); }
        var cssTexts = [];
        var elements = this.querySelectorAll("*");
        for ( var i = 0; i < elements.length; i++ ) {
            var e = elements[i];
            if (!noStyleTags[e.tagName]) {
                var computedStyle = getComputedStyle(e);
                var defaultStyle = getDefaultStyleByTagName(e.tagName);
                cssTexts[i] = e.style.cssText;
                for (var ii = 0; ii < computedStyle.length; ii++) {
                    var cssPropName = computedStyle[ii];
                    if (computedStyle[cssPropName] !== defaultStyle[cssPropName]) {
                        e.style[cssPropName] = computedStyle[cssPropName];
                    }
                }
            }
        }
        var result = this.outerHTML;
        for ( var i = 0; i < elements.length; i++ ) {
            elements[i].style.cssText = cssTexts[i];
        }
        return result;
    }
})();
74
Luc125

Document.getElementsByTagName( 'body')[0] .innerHTMLを実行することはできませんか?インスペクターで変更を行った後、コンソールに上記のJavaScriptを入力すると、更新されたHTMLが返されます。

編集:私はちょうどそのスクリプトを関数に入れてonclickイベントにアタッチしようとしました。インスペクターでいくつかの更新を行い、ボタンをクリックすると、機能しました:

HTML

<button onclick="printDOM()">Print DOM</button>

Javascript

function printDOM() {
    console.log(document.getElementsByTagName('body')[0].innerHTML) ;
}
9
squidbe

ページ全体をキャプチャする場合は、すべての非インラインスタイルシートを取得してインライン化する方が簡単です。

受け入れられた答えのアプローチは素晴らしいですが、非常に遅く、文書全体に触れます。

スタイルを含むページをキャプチャするために、次のアプローチを取りました。

  1. document.documentElement.outerHTML;

  2. document.styleSheets AP​​Iからすべてのスタイルシートを取得します

以下に沿って:

function captureCss(){
    var cssrules = "";
    var sheets = document.styleSheets;
    for(var i = 0; i<sheets.length; i++){
        if(!sheets[i].disabled && sheets[i].href != null) { // or sheets[i].href.nodeName == 'LINK'
            if(sheets[i].rules == null){ // can be null because of cross Origin policy
                try{
                    var fetched = XHR GET(sheets[i].href); // works nicely because it hits the cache
                    if(fetched){
                        cssrules += "<style>\n"+fetched+"\n</style>\n"
                    }
                }catch(e){
                    console.log(e);
                }
                continue;
            }
            for(var j=0;j<sheets[i].rules.length;j++){
                cssrules += "<style>\n"+sheets[i].rules[j].cssText+"\n</style>\n"
            }
        }
    }
    return cssrules;
}
  1. キャプチャしたcssrulesouterHtml htmlテキストのヘッダーの最初のものとして追加します

このようにして、自己完結型のスタイル付きページを取得します。

これは明らかに部分的なコンテンツにはあまり適していません。

3
sleeplessnerd

Luc125の答えに基づいて、Chromeの開発者ツール拡張機能を作成しました。この拡張機能には、ページフラグメントのスタイルとマークアップをキャプチャするコードが組み込まれています。拡張機能は Chrome Web Store and on Github 。「Computed Styles」出力オプションはその方法を使用します。

Extension Screenshot

2
ifugu

OK、多分私はここで何かを見逃していますが、あなたが望む文字列ではありませんdocument.documentElement.innerHTML? Chromeの簡単なテストでは、開発者ツールで行われた変更をピックアップして、記述したとおりに属性をスタイルします。割り当てられたクラス名は展開されません(たとえば、何 class="superfuntime"はやっています)、しかしあなたの質問を正しく読んでいるなら、あなたはその必要性を述べていません。

1
JURU

Internet Explorer->開発者ツール-> DOM Explorer

要素を選択して右クリック-> "スタイル付きの要素をコピー"

1
Ionut

Google Closure Library に解決策があるかもしれません。

必要なことを行うコードがあります。つまり、CSSルールを計算して、dom内の現在の位置以外の要素の外観を再現します(その場合、スタイルをiframeに転送して使用する必要があります)シームレスなインラインエディター)。

ソースファイルstyle.js からの引用:

Provides utility routines for copying modified
CSSRule objects from the parent document into iframes so that any
content in the iframe will be styled as if it was inline in the parent
document.

<p>
For example, you might have this CSS rule:

#content .highlighted { background-color: yellow; }

And this DOM structure:

<div id="content">
  <iframe />
</div>

Then inside the iframe you have:

<body>
<div class="highlighted">
</body>

If you copied the CSS rule directly into the iframe, it wouldn't match the
.highlighted div. So we rewrite the original stylesheets based on the
context where the iframe is going to be inserted. In this case the CSS
selector would be rewritten to:

body .highlighted { background-color: yellow; }
</p>
1
alienhard

Chrome機能-DOMの印刷:
--dump-domフラグの印刷document.body.innerHTML標準出力へ:

chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/

読み取り 詳細

0
saulsluz