Extract the current DOM and print it as a string, with styles intact

前端 未结 8 1632
执笔经年
执笔经年 2020-12-07 08:19

I\'d like to be able to take my DOM, as is, and convert it to a string. Let\'s say I open up the inspector and make a change to the margin-left property of a particular ele

相关标签:
8条回答
  • 2020-12-07 08:57

    Chrome feature - Printing the DOM:
    The --dump-dom flag prints document.body.innerHTML to stdout:

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

    Read more

    0 讨论(0)
  • 2020-12-07 09:05

    OK, maybe I'm missing something here, but isn't the string you want just document.documentElement.innerHTML? A quick test w/ Chrome verifies that it picks up the changes made in the Developer Tools to style attributes as you describe. Assigned class names aren't expanded (e.g., you'll have no idea what class="superfuntime" is doing), but if I'm reading your question correctly, you haven't stated a need for that.

    0 讨论(0)
  • 2020-12-07 09:09

    Can't you just do document.getElementsByTagName('body')[0].innerHTML? When I make changes in the inspector and then enter the above javascript in the console, it returns the updated HTML.

    EDIT: I just tried putting that script in a function and attaching it to an onclick event. Made some updates in the inspector, clicked button, and it worked:

    HTML

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

    Javascript

    function printDOM() {
        console.log(document.getElementsByTagName('body')[0].innerHTML) ;
    }
    
    0 讨论(0)
  • 2020-12-07 09:09

    Based on Luc125's answer, I've created a developer tools extension for Chrome that incorporates that code for capturing styles and markup for a page fragment. The extension is in the Chrome Web Store and is on Github. The "Computed Styles" output option uses that method.

    Extension Screenshot

    0 讨论(0)
  • 2020-12-07 09:10

    In case you want to capture the whole page, it is easier to just get all non-inline stylesheets and inline them.

    The approach in the accepted answer is magnificent, but quite slow and touches the whole document.

    I took the following approach to capture a page including style:

    1. document.documentElement.outerHTML;

    2. get all stylesheets from the document.styleSheets API

    Along the lines of:

    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. Add the captured cssrules as the first thing of the header in the outerHtml html text

    This way you get a self contained styled page.

    This is obviously less applicable for partial content.

    0 讨论(0)
  • 2020-12-07 09:19

    I think this could be a solution (it took me nearly a whole day!).

    It returns a string representing the DOM of any element, with all external styles included in the "style" attributes except default values, and does not permanently modify that element.

    For example: console.log(document.body.serializeWithStyles());

    You can load this code in Web Inspector command line or from a script tag in the body element but NOT in the head element because it requires the existence of document.body.

    I have tested it on desktop Safari 5 (I don't have the mobile version).

    It works like this:

    For each element in the DOM:
    1) caching the value of style.cssText property, which represents the inline style, in an array;
    2) calling getComputedStyle on the element;
    3) checking if we have the css default values lookup table corresponding to this element's tag name;
    4) building it if not;
    5) iterating through the result, finding which values are non default using the lookup table;
    6) applying those non default style values to the element.
    Then storing the outerHTML as the result;
    For each element, restoring the inline styles from the cache;
    Returning the previously stored result.

    The 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;
        }
    })();
    0 讨论(0)
提交回复
热议问题