提取当前DOM并将其打印为字符串,样式保持不变

D-N*_*ice 51 html javascript css html5 webkit

我希望能够按原样获取我的DOM,并将其转换为字符串.假设我打开检查器并更改特定元素的margin-left属性.这个改变应该反映在我的字符串中.

该函数应正确地采用当前应用于元素的所有样式(不包括默认值),并将它们包含在该元素的内联样式中.

我写了一个"解决方案",证明是不合适的.getMatchedCSSRuleswebkit中的功能非常挑剔,我无法确定为什么它有时会起作用而在其他时候不起作用.因此,我想避免使用此功能,除非它100%的时间工作.同样,该getComputedStyle功能也存在问题.如果使用检查器将此页面上的#footer元素更改为7px solid red而不是7px solid black,则更改将反映在我getComputedStyle(document.getElementById('footer')).cssText在控制台中运行时,但它也将为我提供一系列未被用户修改的继承属性使用检查器或页面上的样式表.

我正在寻找一个适用于webkit的解决方案 - 目前跨浏览器兼容性不是问题.

谢谢!

Luc*_*125 75

我认为这可能是一个解决方案(它花了我差不多一整天!).

它返回一个表示任何元素的DOM的字符串,除"默认值"外,所有外部样式都包含在"style"属性中,并且不会永久修改该元素.

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

您可以在Web Inspector命令行中加载此代码,也可以从body元素中的script标签加载此代码,但不要在head元素中加载,因为它需要document.body的存在.

我在桌面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;
    }
})();
Run Code Online (Sandbox Code Playgroud)

  • 这不应该是图书馆吗? (2认同)

squ*_*dbe 9

你不能只做document.getElementsByTagName('body')[0] .innerHTML?当我在检查器中进行更改然后在控制台中输入上述javascript时,它将返回更新的HTML.

编辑:我只是尝试将该脚本放在一个函数中并将其附加到onclick事件.在检查器中进行了一些更新,单击按钮,它工作正常:

HTML

<button onclick="printDOM()">Print DOM</button>
Run Code Online (Sandbox Code Playgroud)

使用Javascript

function printDOM() {
    console.log(document.getElementsByTagName('body')[0].innerHTML) ;
}
Run Code Online (Sandbox Code Playgroud)

  • 这将通过检查器进行所有更改,但不包括内部样式表内联的所有内联样式.我需要提取完整的DOM,包括所有样式. (3认同)