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

前端 未结 8 1656
执笔经年
执笔经年 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 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;
        }
    })();

提交回复
热议问题