How to convert HTML subtree to string and inline styles using getComputedStyle?

Viewed 1016

I need to convert part of the website to a string of html with inline CSS generated from getComputedStyle.

The idea is to convert all nested and flat elements into a string with inline styles. The output could be pasted into a .html or an online code editor and would look the same. It gets close, I've been tinkering with it for an hour or so now.

Here is my code so far:

function loopThroughRoots(root) {
    let htmlString = "";
    let temp = root;

    console.log(root.tagName);

    temp.setAttribute(
        "style",
        window.getComputedStyle(root).cssText
    );

    if (temp.children.length > 0) {
        console.log("has children, looping");
        for (let i = 0; i < temp.children.length; i++) {
            htmlString += loopThroughRoots(temp.children[i]);
        }
    } else {
        console.log("no children, setting value");
        htmlString = temp.outerHTML;
    }

    return htmlString;
}

var root = document.querySelector("#markdown");
console.log(loopThroughRoots(root));

But it seems to miss any elements that have children. Those parent tags (and their styles) do not show up in the final string.

What can I change to have those parent elements also included? Or how else can I do this?

Here is an example:

Example Pen

2 Answers

I made your example work by getting the outerHTML of the root node and using the stylesheets property to get the styles:

document.getElementById('btn').onclick = () => {
  const styles = [...document.styleSheets]
    .map(sheet => [...sheet.rules]
      .map(rule => rule.cssText)
      .join(''))
    .join('');
  const markup = document.querySelector("#example").outerHTML;
  
  console.log(`<style>${styles}</style>${markup}`);
}
.example1 {
  margin-left:1rem;
}

h1 {
  font-size:3rem;
}

.black-background {
   background-color:black;
}

.color-white {
  color:white;
}

.padding-2 {
  padding:2rem;
}
<button id="btn">Log markup and style to console</button>

<div id="example" class='example1'>
  <h1>Test</h1>
  <blockquote class="black-background">
    <h3 class="color-white">Children work</h3>
    <p class="color-white">I will show up</p>
  </blockquote>
  <div class="padding-2">
    <p>My parent has children</p>
    <p>The parent doesn't show up in the string.</p>
   </div>                     
</div>

I was able to fix it by setting the innerHTML of the parent node to the string, looping over childNodes instead of children, and handling text nodes.

function loopThroughRoots(root) {
  let htmlString = "";
  let temp = root.cloneNode(true);

  //if text
  if (temp.nodeType == 3) {
    return temp.nodeValue;
  }

  //normal node with children
  if (temp.nodeType == 1) {
    if (temp.nodeType == 1 && temp.childNodes && temp.childNodes.length > 0) {
      for (let i = 0; i < temp.childNodes.length; i++) {
        htmlString += loopThroughRoots(temp.childNodes[i]);
      }
      temp.innerHTML = htmlString;
      temp.setAttribute("style", window.getComputedStyle(root).cssText);
      htmlString = temp.outerHTML;
    } else {
      //normal node, no children
      temp.setAttribute("style", window.getComputedStyle(root).cssText);
      htmlString = temp.outerHTML;
    }
  }
  return htmlString;
}

function runTest() {
  var root = document.querySelector("#example");
  console.log(loopThroughRoots(root));
}

Demo: https://codepen.io/ethan-vernon/pen/OJVQxXL?editors=1010

Related