页面加载后DOM重构:解决querySelectorAll重复选中元素引发的无限递归问题
Let's break down the problems in your code and fix them step by step:
Core Problem: Infinite Recursion
Your querySelectorAll('h1, p') selects all <h1> and <p> elements in the entire subtree of #content—including those already wrapped inside .desc-container divs. This means every recursive call keeps picking up the same wrapped elements, leading to an infinite loop.
Fix 1: Target Only Direct Child Elements
Instead of selecting all descendants, use the :scope pseudo-class to target only direct child elements of #content. This ensures you only process elements that haven't been wrapped yet:
const parentContent = parentBlock.querySelectorAll(':scope > h1, :scope > p')
Fix 2: Correct the Element Matching Logic
Your getIndex function uses a flawed regex (new RegExp('h?')) which matches almost any element (since h? allows zero or one "h" characters). Replace this with a direct check for <h1> elements to properly identify the start of each group:
const getIndex = nodeList => { const indexes = { start: -1, end: -1 } for (let i = 0; i < nodeList.length; i++) { // Look for the first h1 as the group start if (nodeList[i].localName === 'h1' && indexes.start < 0) { indexes.start = i } // Stop at the first p after the h1 if (indexes.start !== -1 && nodeList[i].localName === 'p') { indexes.end = i break } } return indexes }
Fix 3: Fix the DOM Structure Assembly
Your original code had a bug in how you appended nodes: mainNode.appendChild(titleNode).appendChild(bodyNode) appends bodyNode inside titleNode, not directly to mainNode. Split this into two separate calls to match your desired structure:
mainNode.appendChild(titleNode) mainNode.appendChild(bodyNode)
Full Revised Code
Here's the complete working version of your function:
const createComplexNode = () => { const parentBlock = document.querySelector('#content') // Only select direct child h1/p elements const parentContent = parentBlock.querySelectorAll(':scope > h1, :scope > p') if (parentContent.length === 0) return const mainNode = document.createElement('div') const titleNode = document.createElement('div') const bodyNode = document.createElement('div') mainNode.className = 'desc-container' titleNode.className = 'desc-head' bodyNode.className = 'desc-body' const indexes = getIndex(parentContent) // Exit if no valid group found if (indexes.start === -1) return // Move the h1 into titleNode (removes it from parentBlock's direct children) titleNode.appendChild(parentContent[indexes.start]) // Move the corresponding p into bodyNode if (indexes.end !== -1) { bodyNode.appendChild(parentContent[indexes.end]) } // Assemble the container correctly mainNode.appendChild(titleNode) mainNode.appendChild(bodyNode) // Add the new container to parentBlock parentBlock.appendChild(mainNode) // Recurse to process remaining direct child elements return createComplexNode() } const getIndex = nodeList => { const indexes = { start: -1, end: -1 } for (let i = 0; i < nodeList.length; i++) { if (nodeList[i].localName === 'h1' && indexes.start < 0) { indexes.start = i } if (indexes.start !== -1 && nodeList[i].localName === 'p') { indexes.end = i break } } return indexes }
Why This Works
:scope > h1, :scope > pensures we only process elements that are direct children of#content—already wrapped elements are nested inside.desc-container, so they're ignored in subsequent recursive calls.- When we use
appendChildon elements fromparentContent, we're moving them from their original position in#contentto the new container. This means each recursive call will only see the next unprocessed<h1>and<p>pair. - The corrected node assembly ensures the DOM structure matches exactly what you specified.
内容的提问来源于stack exchange,提问作者Iangyl

