wake-up-neo.net

Extrahieren Sie das aktuelle DOM und drucken Sie es als String mit intakten Formaten

Ich möchte mein DOM so nehmen können, wie es ist, und es in eine Zeichenfolge konvertieren. Nehmen wir an, ich öffne den Inspektor und ändere die Eigenschaft margin-left eines bestimmten Elements. Diese Änderung sollte sich in meiner Zeichenfolge widerspiegeln.

Die Funktion sollte alle aktuell auf ein Element angewendeten Stile (mit Ausnahme der Standardwerte) korrekt übernehmen und in den Inline-Stil für dieses Element einschließen.

Ich habe eine "Lösung" geschrieben, die sich als unzureichend erwiesen hat. Die getMatchedCSSRules-Funktion in Webkit ist äußerst wählerisch, und ich konnte nicht feststellen, warum es manchmal funktioniert und manchmal nicht. Daher möchte ich die Verwendung dieser Funktion vermeiden, wenn sie nicht zu 100% funktioniert. In ähnlicher Weise hat die Funktion getComputedStyle eigene Probleme. Wenn Sie mit dem Inspektor das Element #footer auf dieser Seite in 7px solid red und nicht in 7px solid black ändern, wird die Änderung beim Ausführen von getComputedStyle(document.getElementById('footer')).cssText in der Konsole übernommen, es wird jedoch auch ein Host mit geerbten Eigenschaften angezeigt, die von nie geändert wurden entweder der Benutzer, der den Inspektor verwendet, oder anhand der Stylesheets auf der Seite.

Ich suche nach einer Lösung, die mit Webkit funktioniert - Cross-Browser-Kompatibilität ist im Moment kein Thema. 

Vielen Dank!

47
D-Nice

Ich denke, das könnte eine Lösung sein (ich habe fast einen ganzen Tag gebraucht!).

Es gibt eine Zeichenfolge zurück, die das DOM eines beliebigen Elements darstellt, Alle externen Stile, die in den Attributen "style" enthalten sind, mit Ausnahme der Standardwerte , Ändern dieses Element nicht dauerhaft.

Zum Beispiel: console.log(document.body.serializeWithStyles());

Sie können diesen Code in die Web Inspector-Befehlszeile oder über ein Skripttag im body-Element laden, NICHT jedoch in das head-Element, da document.body erforderlich ist.

Ich habe es auf Desktop Safari 5 getestet (ich habe keine mobile Version). 

Es funktioniert so:

Für jedes Element im DOM:
1) Zwischenspeichern des Werts der style.cssText -Eigenschaft, die den Inline-Stil in einem Array darstellt;
2) Aufruf von getComputedStyle für das Element;
3) Überprüfen, ob die css-Standardwert-Nachschlagetabelle für den Tagnamen dieses Elements vorhanden ist;
4) Bauen wenn nicht;
5) Durchlaufen des Ergebnisses, Ermitteln, welche Werte mit der Nachschlagetabelle nicht voreingestellt sind;
6) Anwenden dieser nicht standardmäßigen Stilwerte auf das Element.
Speichern der äußeren HTML als Ergebnis;
Wiederherstellen der Inline-Stile für jedes Element aus dem Cache;
Das zuvor gespeicherte Ergebnis zurückgeben.

Der Code:

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;
    }
})();
72
Luc125

Können Sie nicht einfach document.getElementsByTagName ('body') [0] .innerHTML ausführen? Wenn ich im Inspektor Änderungen vornehme und dann das obige Javascript in die Konsole eingebe, wird der aktualisierte HTML-Code zurückgegeben.

EDIT: Ich habe gerade versucht, das Skript in eine Funktion zu setzen und es einem Onclick-Event zuzuordnen. Im Inspektor wurden einige Updates vorgenommen, auf die Schaltfläche geklickt, und es funktionierte:

HTML

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

Javascript

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

Für den Fall, dass Sie die gesamte Seite erfassen möchten, ist es einfacher, alle Nicht-Inline-Stylesheets abzurufen und sie einzublenden.

Die Annäherung in der akzeptierten Antwort ist großartig, aber ziemlich langsam und berührt das gesamte Dokument.

Ich habe den folgenden Ansatz gewählt, um eine Seite mit Stil aufzunehmen:

  1. document.documentElement.outerHTML;

  2. holen Sie sich alle Stylesheets von der document.styleSheets-API

Nach den Zeilen von:

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. Fügen Sie die erfasste cssrules als erstes der Überschrift im outerHtml-HTML-Text hinzu

Auf diese Weise erhalten Sie eine eigenständige Stilseite.

Dies gilt natürlich für Teilinhalte weniger.

3
sleeplessnerd

OK, vielleicht fehlt mir hier etwas, aber ist die gewünschte Zeichenfolge nicht nur document.documentElement.innerHTML? Bei einem Schnelltest mit Chrome wird überprüft, ob die in den Entwicklertools vorgenommenen Änderungen an den von Ihnen beschriebenen Attributen vorgenommen werden. Zugewiesene Klassennamen werden nicht erweitert (z. B. haben Sie keine Ahnung, was class="superfuntime" tut), aber wenn ich Ihre Frage richtig lese, haben Sie keine Notwendigkeit dafür angegeben. 

1
JURU

Basierend auf der Antwort von Luc125 habe ich eine Erweiterung für Entwicklertools für Chrome erstellt, die diesen Code zum Erfassen von Stilen und Markierungen für ein Seitenfragment enthält. Die Erweiterung befindet sich im Chrome Web Store und ist auf Github . Die Ausgabeoption "Berechnete Stile" verwendet diese Methode.

Extension Screenshot

1
ifugu

Internet Explorer -> Entwicklerwerkzeuge -> DOM Explorer

Element auswählen und Rechtsklick -> "Element mit Stilen kopieren".

1
Ionut

Vielleicht hat die Google Closure Library eine Lösung für Sie.

Es gibt Code, der das zu tun scheint, was Sie brauchen, dh die CSS-Regeln zu berechnen, um das gleiche Erscheinungsbild eines Elements außerhalb seiner aktuellen Position im Dom zu reproduzieren (in diesem Fall müssen sie Stile in einen iframe übertragen, um es als zu verwenden ein nahtloser Inline-Editor).

Zitieren aus der Quelldatei 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-Funktion - DOM drucken:
Das Flag --dump-dom gibt document.body.innerHTML in stdout aus:

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

Lese mehr

0
saulsluz