You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在HTA中正确遍历li标签构建JSON对象

问题

我正在开发一款可动态构建与编辑JSON文件的HTA工具,通过<li>和<ul>标签搭建UI界面,每个数据类型对应独立模板(从模板文件库调用),基础结构如下:

<li type='list'> 
   <span>Name</span>
   <div> bunch of html that has hidden edit pop up windows and other controls </div>
   <li><ul> sub entries </ul></li>
</li>

但遍历UI生成JSON时,尽管<li><ul></ul></li>嵌套在父<li>内,DOM遍历却将所有<li>视为同级元素。尝试过jQuery与DOM选择器组合,但总是同时选中父、子<li>。曾尝试添加条目时同步构建对象,但代码逻辑混乱,不利于后续维护。现咨询:

  • 是列表元素的构建方式有误,还是DOM遍历方法不正确?
  • 是否应该使用<li>和<ul>以外的元素?

以下是简化示例代码:

<html>
    <head>
        <meta charset="UTF-8" http-equiv="x-ua-compatible" content="ie=edge">
        <title>JSON Builder</title>         
    </head>
    
    <body>
        <button style="width: 100%" onclick="getChildren()">Get</button>
        <div style="height: 100%; width: 100%;"  >
            <ul id="options" style="height: 100%; border: 2px ridge; border-radius: 10px;">
            
                <li id="entry_X" type="string" >
                    <div style="width: 15em;" >
                        <span id="entry_name_X"  >Root 0</span>     
                    </div>
                    
                    <li>
                        <ul>
                        
                            <li id="entry_1" type="string" >
                                <div style="width: 15em;" >
                                    <span id="entry_name_1"  >1</span>      
                                </div>
                                
                                <!-- This should be nested inside the li by my understanding but isnt so i was hiding them to get the look i wanted. -->                                
                                <li style='display: none;'>
                                    <ul>
                                    </ul>
                                </li>
                                
                            </li>
                            
                            <li id="entry_2" type="string" >
                                <div style="width: 15em;" >
                                    <span id="entry_name_2"  >2</span>      
                                </div>
                                
                                <li style='display: none;'>
                                    <ul>
                                    </ul>
                                </li>
                                
                            </li>
                                    
                        </ul>
                    </li>
                    
                </li>
                
                <li id="entry_6" type="string" >
                    <div style="width: 15em;" >
                        <span id="entry_name_X"  >Root 1</span>     
                    </div>
                    
                    <li>
                        <ul>
                        
                            <li id="entry_7" type="string" >
                                <div style="width: 15em;" >
                                    <span id="entry_name_7"  >7</span>      
                                </div>
                                
                                <li style='display: none;'>
                                    <ul>
                                    </ul>
                                </li>
                                
                            </li>
                            
                            <li id="entry_8" type="string" >
                                <div style="width: 15em;" >
                                    <span id="entry_name_8"  >8</span>      
                                </div>
                                
                                <li style='display: none;'>
                                    <ul>
                                    </ul>
                                </li>
                                
                            </li>
                                    
                        </ul>
                    </li>
                    
                </li>
                
            </ul>
        </div>
    </body>
    
    <script>
        function getChildren() {
                // This returns a length of 6 items
                var l1 = document.querySelectorAll( 'ul>li' );
                alert( "[ QALL ul>li length ] " + l1.length );
                
                /* 
                    This returns a length of 2 items. 
                    the first item is the div in the first li and the 2nd is 
                    the li inside the first li. 
                    
                    I expected this to return just the 1 li as there is only 1 li in 
                    the options ul but that is not how its working. 
                
                */
                var l1 = document.querySelector( '#options' ).children;
                alert( "[ QSEL options.children length ] " +  l1.length );                              
                //alert( l1[0].innerHTML );
                //alert( l1[1].innerHTML );
                
                
                var l1 = document.querySelectorAll( '#options ul>li' );
                alert( "[ QSEL2 options.children length ] " +  l1.length );

            }
            
    </script>
    
    <script> 
            resizeTo( 690,390 );            
            moveTo(screen.width/2-210,screen.height/2-250);
        </script>   
    
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
</html>

解答

核心问题:HTML结构不符合规范

你的列表嵌套写法存在错误:<li>的直接子节点不能是另一个<li>,必须先嵌套<ul>或<ol>这类列表容器,再在容器内放置子<li>。浏览器遇到不符合规范的嵌套时,会自动修正DOM结构,把错误嵌套的<li>提升为同级元素,这就是遍历所有<li>都被视为同级的根本原因。

比如你原本的错误结构:

<li type='list'> 
   <span>Name</span>
   <div>...</div>
   <li><ul> sub entries </ul></li> <!-- 错误:li直接嵌套li -->
</li>

浏览器会自动修正为:

<li type='list'> 
   <span>Name</span>
   <div>...</div>
</li>
<li><ul> sub entries </ul></li> <!-- 被提升为同级元素 -->

正确的列表嵌套结构

要实现层级嵌套,需在父<li>内部先放置内容,再添加<ul>容器,然后在<ul>中放置子<li>:

<li type='list'> 
   <span>Name</span>
   <div> bunch of html that has hidden edit pop up windows and other controls </div>
   <!-- 正确写法:父li内嵌套ul,再在ul里放子li -->
   <ul>
     <li>sub entries</li>
   </ul>
</li>

对应你的示例代码,修正后的父条目结构应为:

<li id="entry_X" type="string" >
    <div style="width: 15em;" >
        <span id="entry_name_X"  >Root 0</span>     
    </div>
    <!-- 直接用ul作为父li的子元素,而非先套li -->
    <ul>
        <li id="entry_1" type="string" >
            <div style="width: 15em;" >
                <span id="entry_name_1"  >1</span>      
            </div>
            <!-- 子条目自己的子列表,直接嵌套ul -->
            <ul style='display: none;'></ul>
        </li>
        <li id="entry_2" type="string" >
            <div style="width: 15em;" >
                <span id="entry_name_2"  >2</span>      
            </div>
            <ul style='display: none;'></ul>
        </li>
    </ul>
</li>

遍历方法修正

结构规范后,可通过精准选择器遍历层级:

  • 选中顶级条目:document.querySelectorAll('#options > li'),仅匹配#options的直接子<li>
  • 选中某父条目下的子条目:parentLiElement.querySelectorAll('> ul > li'),或parentLiElement.querySelector('ul').children

是否需要替换<ul>/<li>?

不需要。<ul>/<li>本身就是为层级列表设计的语义化标签,只要结构规范,完全能满足需求。替换为div等元素反而会失去语义化优势,增加样式和逻辑复杂度。

额外建议

动态增删条目时,给每个条目对应的子列表添加明确类名(比如entry-children),能进一步降低遍历出错概率:

<li type="string" class="json-entry">
  <div>...</div>
  <ul class="entry-children" style="display:none;"></ul>
</li>

之后用document.querySelectorAll('.json-entry > .entry-children > li')即可精准选中子条目。

内容的提问来源于stack exchange,提问作者tokageKitayama

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 16:46:13