Skip to content Skip to sidebar Skip to footer

How To Extract Nested Html Element To Same Level In Js

I use javascript and have a div element with some html tags inside it like below, some of the elements are nested and I want to keep them in the same level, first html is like this

Solution 1:

You can clone original node using .cloneNode() with parameter true; iterate the .childNodes of parent <div>, if node .previousElementSibling is not null and .parentElement is not original parent element passed to function push .parentElement .tagName and .textContent to array, else .nodeName is #text, push .textContent to an array; if .nodeName does not equal #text and only #text nodes exist within element, push .outerHTML to array; if node .tagName does not equal #text and node .firstChild node is #text, push node .tagName, .firstChild .textContent, closing tag to array, remove .firstChild; else recursively call function

const clone = document.querySelector("div").cloneNode(true);

let flattenNodes = (el, not = el, res = []) => {
  for (let node of el.childNodes) {
    if (node.nodeName === "#text" && !node.childNodes.length) {
      if (node.previousElementSibling && node.parentElement !== not) {
        let tag = node.parentElement.tagName.toLowerCase();
        res.push(`<${tag}>${node.textContent}</${tag}>`);
      } else {
        res.push(node.textContent);
      }
    }
    if (node.nodeName !== "#text" 
      && Array.from(node.childNodes).every(e => e.nodeName === "#text")) {
        res.push(node.outerHTML);
    } else {
      if (node.tagName && node.tagName !== "#text" 
        && node.firstChild.nodeName === "#text") {
          let tag = node.tagName.toLowerCase();
          res.push(`<${tag}>${node.firstChild.textContent}</${tag}>`);
          node.firstChild.remove();
          flattenNodes(node, not, res);
      }
    }
  }
  return res
}

let res = flattenNodes(clone);

document.querySelector("pre")
.textContent = res.join("");
<div>
  <p>
    some text on here 1
    <i>
      some italic 
      <b>text</b>
      here
    </i> text text text
  </p>
  <b>
    some bold text on here
  </b>
</div>

<pre></pre>

Solution 2:

How often does this operation need to happen? Is it a performance hazard or is it a rare occasion?

If it's not a bottleneck, you can just select all descendants of your div and append them to the div. When you append an existing node, it will be automatically moved to the appropriate position.

const myDiv = document
      .querySelector('#my-div')

const allDescendants = Array.from(myDiv.getElementsByTagName('*'))

allDescendants.forEach(d => {
  myDiv.appendChild(d);
});

console.log(myDiv.innerHTML);
<div id="my-div">
  <p>
    some text on here 1
    <i>
      some italic 
      <b>text</b>
      here
    </i> text text text
  </p>
  <b>
    some bold text on here
  </b>
</div>

Not sure if this is intentional but based on your example, you only want to move elements and leave other nodes such as text nodes where they were. A specific example is the here text portion in the first <i>.

To achieve this, you can perform a pre-order DFS through all descendants, and build the new flat tree. Once finished, replace the old element with the new one.

const myDiv = document
      .querySelector('#my-div')

const flatDiv = document.createElement('div')
function recurseAndFlatten(parent) {
  parent.childNodes.forEach(child => {
    if (child.nodeType === Node.ELEMENT_NODE) {
      flatDiv.appendChild(child);
      recurseAndFlatten(child);
    }
  });
}

recurseAndFlatten(myDiv);

myDiv.replaceWith(flatDiv);

console.log(flatDiv.innerHTML);
<div id="my-div">
  <p>
    some text on here 1
    <i>
      some italic 
      <b>text</b>
      here
    </i> text text text
  </p>
  <b>
    some bold text on here
  </b>
</div>

Post a Comment for "How To Extract Nested Html Element To Same Level In Js"