Javascript 提取当前 DOM 并将其打印为字符串,样式完整
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6209161/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Extract the current DOM and print it as a string, with styles intact
提问by D-Nice
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 element. That change should be reflected in my string.
我希望能够按原样获取我的 DOM,并将其转换为字符串。假设我打开检查器并更改特定元素的 margin-left 属性。这种变化应该反映在我的字符串中。
The function should properly take all the styles currently applied to an element (excluding default values) and include them in the inline style for that element.
该函数应正确采用当前应用于元素的所有样式(不包括默认值),并将它们包含在该元素的内联样式中。
I have written a 'solution' which has proven to be inadequate. The getMatchedCSSRules
function in webkit is extremely finicky, and I haven't been able to determine why it sometimes works and doesn't work other times. Therefore, I would like to avoid using this function unless it works 100% of the time. Similarly, the getComputedStyle
function has problems of its own. If use the inspector to change the #footer element on this page to be 7px solid red
rather than 7px solid black
, the change will be reflected in when I run getComputedStyle(document.getElementById('footer')).cssText
in the console, but it will also give me a host of inherited properties that were never modified by either the user using the inspector or by the stylesheets on the page.
我写了一个“解决方案”,但被证明是不够的。getMatchedCSSRules
webkit 中的函数非常挑剔,我一直无法确定为什么它有时有效而其他时候不起作用。因此,我想避免使用此功能,除非它 100% 的时间都有效。同样,该getComputedStyle
功能也有其自身的问题。如果使用检查器将此页面上的 #footer 元素更改为7px solid red
而不是7px solid black
,则更改将反映在我getComputedStyle(document.getElementById('footer')).cssText
在控制台中运行时,但它也会为我提供许多从未被用户修改的继承属性使用检查器或页面上的样式表。
I am looking for a solution that works with webkit--cross browser compatibility is not an issue at the moment.
我正在寻找一种适用于 webkit 的解决方案——目前跨浏览器兼容性不是问题。
Thank you!
谢谢!
回答by Luc125
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.
它返回一个表示任何元素的 DOM 的字符串,“style”属性中包含除默认值之外的所有外部样式,并且不会永久修改该元素。
For example: console.log(document.body.serializeWithStyles());
例如: 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.
您可以在 Web Inspector 命令行中或从 body 元素中的脚本标记加载此代码,但不能在 head 元素中加载,因为它需要存在 document.body。
I have tested it on desktop Safari 5 (I don't have the mobile version).
我已经在桌面 Safari 5 上对其进行了测试(我没有移动版本)。
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.
对于 DOM 中的每个元素:
1) 将表示内联样式的 style.cssText 属性值缓存在一个数组中;
2)在元素上调用getComputedStyle;
3) 检查我们是否有与此元素的标签名称对应的 css 默认值查找表;
4) 如果没有,则构建它;
5) 遍历结果,使用查找表找出哪些值是非默认值;
6) 将那些非默认样式值应用于元素。
然后将outerHTML作为结果存储;
对于每个元素,从缓存中恢复内联样式;
返回之前存储的结果。
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;
}
})();
回答by squidbe
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.
你不能只做 document.getElementsByTagName('body')[0].innerHTML 吗?当我在检查器中进行更改,然后在控制台中输入上述 javascript 时,它会返回更新后的 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:
编辑:我只是尝试将该脚本放在一个函数中并将其附加到一个 onclick 事件。在检查器中进行了一些更新,单击按钮,它起作用了:
HTML
HTML
<button onclick="printDOM()">Print DOM</button>
Javascript
Javascript
function printDOM() {
console.log(document.getElementsByTagName('body')[0].innerHTML) ;
}
回答by ifugu
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 Storeand is on Github. The "Computed Styles" output option uses that method.
根据 Luc125 的回答,我为 Chrome 创建了一个开发人员工具扩展,其中包含用于捕获页面片段样式和标记的代码。该扩展程序位于Chrome 网上应用店和Github 上。“计算样式”输出选项使用该方法。
回答by sleeplessnerd
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:
我采用以下方法来捕获包含样式的页面:
document.documentElement.outerHTML;
get all stylesheets from the
document.styleSheets
API
document.documentElement.outerHTML;
从
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;
}
- Add the captured
cssrules
as the first thing of the header in theouterHtml
html text
- 将捕获的添加
cssrules
为outerHtml
html文本中header的第一件事
This way you get a self contained styled page.
这样你就可以得到一个自包含样式的页面。
This is obviously less applicable for partial content.
这显然不太适用于部分内容。
回答by Ionut
Internet Explorer -> Developer Tools -> DOM Explorer
Internet Explorer -> 开发者工具 -> DOM Explorer
Select element and right click -> "Copy element with styles".
选择元素并右键单击->“使用样式复制元素”。
回答by alienhard
Maybe the Google Closure Libraryhas a solution for you.
也许Google Closure Library为您提供了解决方案。
There's code that seems to do what you need, i.e., to compute the CSS rules to reproduce the same appearance of an element outside of its current position in the dom (in their case they need that to transfer styles into an iframe to use it as a seamless inline editor).
有一些代码似乎可以满足您的需求,即计算 CSS 规则以重现元素在 dom 中当前位置之外的相同外观(在他们的情况下,他们需要将样式传输到 iframe 中以将其用作一个无缝的内联编辑器)。
Quoting from the source file style.js:
引用源文件 style.js:
/**
* @fileoverview 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>
*/
回答by JURU
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.
好的,也许我在这里遗漏了一些东西,但不是您想要的字符串document.documentElement.innerHTML
吗?使用 Chrome 进行的快速测试验证它是否按照您的描述获取了在开发人员工具中对样式属性所做的更改。分配的类名不会扩展(例如,您将不知道class="superfuntime"
在做什么),但是如果我正确阅读了您的问题,那么您还没有说明需要这样做。