I would like to write JavaScript code that "flattens" the DOM of an arbitrary webpage while preserving the visual appearance (but not necessarily the resize or other dynamic behaviors)
In theory I believe this should simply be a matter of recording the position of each element relative to the window (e.x. PPK's findPos(element)) as well as it's computed CSS styles (e.x. window.getComputedStyle(element).cssText), moving the element from it's parent to a direct child of "body", making it absolutely positioned at the previously recorded position, and setting the recorded CSS attributes directly on the element.
I've had some success with this approach but it's not close to being perfect:
function walkDOM(element, parent, nodes) {
parent = parent || { top : 0, left : 0, depth : 0 };
nodes = nodes || [];
if (element.nodeType === 1) {
var node = findPos(element);
node.element = element;
node.width = element.scrollWidth;
node.height = element.scrollHeight;
node.depth = parent.depth + 1;
node.cssText = window.getComputedStyle(element).cssText;
nodes.push(node);
for (var i = 0; i < element.childNodes.length; i++) {
walkDOM(element.childNodes[i], node, nodes);
}
}
return nodes;
}
// based on http://www.quirksmode.org/js/findpos.html
function findPos(element) {
var position = { left : 0, top : 0 };
if (element.offsetParent) {
do {
position.left += element.offsetLeft;
position.top += element.offsetTop;
} while (element = element.offsetParent);
}
return position;
}
var nodes = walkDOM(document.body);
nodes.forEach(function(node) {
var e = node.element;
if (e !== document.body)
e.parentNode.removeChild(e);
// e.setAttribute("style", node.cssText);
e.style.position = "absolute";
e.style.top = node.top + "px";
e.style.left = node.left + "px";
e.style.width = node.width + "px";
e.style.height = node.height + "px";
e.style.zIndex = node.depth + 1;
if (e !== document.body)
document.body.appendChild(e);
});
I was trying something similar, but I'm flattening only the block-level nodes, as doing all of them will kill the layout (think about an em inside a p, etc.). So I was walking the tree like this:
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT,
function (node) {
if (window.getComputedStyle(node, null).getPropertyValue("display") == "block") {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
},
false
);
var nodeList = new Array();
while(treeWalker.nextNode()) {
nodeList.push(treeWalker.currentNode);
}
Then just iterate them (as you'll have them in proper reverse order) and move them to the body while applying the positioning.
I was for my purposes just recreating simple div boxes for them to study the layout, and I found that I get in trouble with block elements originally positioned in other ones that clip them (overflow hidden), I'm currently looking in using svg masks to reproduce the clipping.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With