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.
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.
I am looking for a solution that works with webkit--cross browser compatibility is not an issue at the moment.
Thank you!
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:
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;
}
})();