醉美水芙蓉 发表于 2026-6-30 21:01:14

马黑黑《遍历与操作容器元素子节点示例》

<style>
    .artBox { margin: 20px auto; font: normal 18px/26px sans-serif; max-width: 1200px; }
    .artBox mark { padding: 2px 6px; background: #ccc; }
    .artBox blockquote { padding: 14px; border: solid gray; border-width: 0 4px; border-radius: 10px; background: #ddd; }
    .artBox p { margin: 10px 0; }
</style>

<div class="artBox">
    <p>要操作容器元素内的子节点,首先需要遍历所有子节点,接着才是去操作特定的子节点。不同的操作目的遍历的方法不一样,下面的探讨不可能包罗万象,但力求做到目标明确、操作精准:每一个实例我们将预设一个需求,然后通过遍历与过滤等手段去操作指定的子节点。具体而言:所有示例都是遍历指定容器元素的子元素节点,找出第一层级或所有层级的 img 标签,并对它们做相应操作。</p>
    <h3>例一:添加所有第一层级img子元素的title属性</h3>
    <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    .mdiv { margin: 20px auto 0; width: 600px; border: 1px solid gray; padding: 12px; }
    .mdiv img { max-width: 150px; }
    .tMid { text-align: center; }
&lt;/style&gt;

&lt;div class="mdiv"&gt;
    &lt;h2 class="tMid"&gt;图片title属性设置演示&lt;/h2&gt;
    &lt;img src="https://638183.freep.cn/638183/small/2025/ff1.webp" alt="" /&gt;
    &lt;img src="https://638183.freep.cn/638183/small/2025/featherjpg.png" alt="" /&gt;
    &lt;img src="https://638183.freep.cn/638183/small/2025/ring.webp" alt="" /&gt;
    &lt;p&gt;【说明】上面的图片初始时没有设置 title 属性,可以点击下方按钮添加该属性。请在点击前、后分别将鼠标指针移到图片上面,看看前后有什么不同。&lt;/p&gt;
&lt;/div&gt;
&lt;p class="tMid"&gt;&lt;button onclick="addTitle2Img();this.disabled='true';"&gt;图片元素加 title 属性&lt;/button&gt;&lt;/p&gt;

&lt;script&gt;
    function addTitle2Img() {
      const div = document.querySelector('.mdiv');
      const elms = div.children;
      [...elms].forEach((elm, key) =&gt; {
            if (elm.tagName === 'IMG') elm.setAttribute('title', `图 ${key}`);
      });
    }
&lt;/script&gt;
    </pre></div>
    <h3>例二:添加所有层级img子元素的title属性</h3>
    <p>方法一:querySelectorAll()方法(最简洁高效)</p>
    <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    .mdiv { margin: 20px auto 0; width: 600px; border: 1px solid gray; padding: 12px; }
    .mdiv img { max-width: 150px; }
    .tMid { text-align: center; }
&lt;/style&gt;

&lt;div class="mdiv"&gt;
    &lt;h2 class="tMid"&gt;图片title属性设置演示&lt;/h2&gt;
    &lt;p&gt;下图在第一层级:&lt;/p&gt;
    &lt;img src="https://638183.freep.cn/638183/small/2025/ff1.webp" alt="" /&gt;
    &lt;p&gt;下图在第二层级:&lt;/p&gt;
    &lt;p class="tMid"&gt;&lt;img src="https://638183.freep.cn/638183/small/2025/featherjpg.png" alt="" /&gt;&lt;/p&gt;
    &lt;div&gt;
      &lt;p&gt;下图在第三层级:&lt;/p&gt;
      &lt;p class="tMid"&gt;&lt;img src="https://638183.freep.cn/638183/small/2025/ring.webp" alt="" /&gt;&lt;/p&gt;
    &lt;/div&gt;
    &lt;p&gt;【说明】上面的图片初始时没有设置 title 属性,可以点击下方按钮添加该属性。请在点击前、后分别将鼠标指针移到图片上面,看看前后有什么不同。&lt;/p&gt;
&lt;/div&gt;
&lt;p class="tMid"&gt;&lt;button onclick="addTitle2Img(document.querySelector('.mdiv'));this.disabled='true';"&gt;图片元素加 title 属性&lt;/button&gt;&lt;/p&gt;

&lt;script&gt;
    // 使用 querySelectorAll()方法拿到所有img元素节点并一一添加itle属性
    function addTitle2Img(elm) {
      const imgs = elm.querySelectorAll('img'); // 获取元素下所有img子元素
      imgs.forEach((img, key) =&gt; img.setAttribute('title', `图 ${key + 1}`));
    }
&lt;/script&gt;
    </pre></div>   
    <p>方法二:递归遍历元素节点(简洁但性能略差、容易爆栈)</p>
    <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    .mdiv { margin: 20px auto 0; width: 600px; border: 1px solid gray; padding: 12px; }
    .mdiv img { max-width: 150px; }
    .tMid { text-align: center; }
&lt;/style&gt;

&lt;div class="mdiv"&gt;
    &lt;h2 class="tMid"&gt;图片title属性设置演示&lt;/h2&gt;
    &lt;p&gt;下图在第一层级:&lt;/p&gt;
    &lt;img src="https://638183.freep.cn/638183/small/2025/ff1.webp" alt="" /&gt;
    &lt;p&gt;下图在第二层级:&lt;/p&gt;
    &lt;p class="tMid"&gt;&lt;img src="https://638183.freep.cn/638183/small/2025/featherjpg.png" alt="" /&gt;&lt;/p&gt;
    &lt;div&gt;
      &lt;p&gt;下图在第三层级:&lt;/p&gt;
      &lt;p class="tMid"&gt;&lt;img src="https://638183.freep.cn/638183/small/2025/ring.webp" alt="" /&gt;&lt;/p&gt;
    &lt;/div&gt;
    &lt;p&gt;【说明】上面的图片初始时没有设置 title 属性,可以点击下方按钮添加该属性。请在点击前、后分别将鼠标指针移到图片上面,看看前后有什么不同。&lt;/p&gt;
&lt;/div&gt;
&lt;p class="tMid"&gt;&lt;button onclick="addTitle2Img(document.querySelector('.mdiv'));this.disabled='true';"&gt;图片元素加 title 属性&lt;/button&gt;&lt;/p&gt;

&lt;script&gt;
    let total = 0; // 当前图片序号
   
    // 递归遍历所有子元素节点 :碰上img元素节点时加title属性
    function addTitle2Img(node) {
      // 若节点标签名是 IMG则设置 title 属性,同时 total 自增
      if (node.tagName === 'IMG') node.setAttribute('title', `图 ${++total}`);
      for (let j = 0; j &lt; node.children.length; j ++) {
            addTitle2Img(node.children); // 往内遍历其它层级的节点
      }
    }
&lt;/script&gt;
    </pre></div>
    <p>方法三:使用 treeWalker API(抽象但更优雅、性能消耗低、不会爆栈)</p>
    <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    .mdiv { margin: 20px auto 0; width: 600px; border: 1px solid gray; padding: 12px; }
    .mdiv img { max-width: 150px; }
    .tMid { text-align: center; }
&lt;/style&gt;

&lt;div class="mdiv"&gt;
    &lt;h2 class="tMid"&gt;图片title属性设置演示&lt;/h2&gt;
    &lt;p&gt;下图在第一层级:&lt;/p&gt;
    &lt;img src="https://638183.freep.cn/638183/small/2025/ff1.webp" alt="" /&gt;
    &lt;p&gt;下图在第二层级:&lt;/p&gt;
    &lt;p class="tMid"&gt;&lt;img src="https://638183.freep.cn/638183/small/2025/featherjpg.png" alt="" /&gt;&lt;/p&gt;
    &lt;div&gt;
      &lt;p&gt;下图在第三层级:&lt;/p&gt;
      &lt;p class="tMid"&gt;&lt;img src="https://638183.freep.cn/638183/small/2025/ring.webp" alt="" /&gt;&lt;/p&gt;
    &lt;/div&gt;
    &lt;p&gt;【说明】上面的图片初始时没有设置 title 属性,可以点击下方按钮添加该属性。请在点击前、后分别将鼠标指针移到图片上面,看看前后有什么不同。&lt;/p&gt;
&lt;/div&gt;
&lt;p class="tMid"&gt;&lt;button onclick="addTitle2Img(document.querySelector('.mdiv'));this.disabled='true';"&gt;图片元素加 title 属性&lt;/button&gt;&lt;/p&gt;

&lt;script&gt;
    // 使用 treeWalker API遍历所有子元素节点 :碰上img元素节点时加title属性
    function addTitle2Img(elm) {
      let total = 0; // 当前图片序号
      let walker = document.createTreeWalker(
            elm, // 元素
            NodeFilter.SHOW_ELEMENT, // 节点过滤器 :只要元素节点
            // 过滤器 :只要img元素
            { acceptNode: (node) =&gt; node.tagName === 'IMG' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP },
      );
      while (walker.nextNode()) {
            const node = walker.currentNode;
            node.setAttribute('title', `图 ${++total}`);
      }
    }
&lt;/script&gt;
    </pre></div>
    <blockquote>
      <p><b>递归和TreeWalker:</b></p>
      <ul>
            <li>递归的核心:访问一个节点,处理它,然后依次访问它的子节点、孙节点(深度优先)。</li>
            <li>TreeWalker 的实现:它内部维护了一个“遍历状态栈”(逻辑栈)。调用 nextNode() 时,它内部执行的就是标准的深度优先遍历(DFS)算法逻辑,只不过它把“递归的压栈/出栈”操作隐藏在了 API 内部,通过 currentNode 指针来记录位置。</li>
      </ul>
    </blockqukote>
</div>

<script type="module">
    import hl from 'https://638183.freep.cn/638183/web/js/hl.js';
    const divs = document.querySelectorAll('.hEdiv');
    const pres = document.querySelectorAll('.hEpre');
    divs.forEach((div,idx) => hl.hl(div, pres));
</script>

klxf 发表于 2026-7-1 08:20:42

长知识了,谢谢分享
页: [1]
查看完整版本: 马黑黑《遍历与操作容器元素子节点示例》