【问题标题】:How do I select the innermost element?如何选择最里面的元素?
【发布时间】:2010-11-15 21:21:51
【问题描述】:

在 jQuery 中,我将如何尽可能地下降到 HTML 树中?

为简单起见,我只有一条向下的路径。

(相关但有好处:如何找到具有多个向下路径的最深元素?)

<html>  
  <table id="table0">  
    <tr>  
      <td id="cell0">  
        <div class"simple">  I want to change this information </div>  
      </td>
    </tr>  
  </table>  
</html>

我想更改名为 cell0 的单元格的最里面的 HTML,但我不一定知道里面所有类的名称。在不知道这些名字的情况下可以选择这么远吗?

非常感谢!

【问题讨论】:

  • 如果&lt;td&gt; 中有两个兄弟&lt;div&gt; 会发生什么?
  • 我知道您的示例代码只是一个示例,但我觉得我应该指出它是无效的 HTML,因为您有一个 &lt;table&gt;&lt;td&gt;,它们之间没有 &lt;tr&gt;
  • @Spudley:谢谢Spudley,我修好了。
  • @cdhowie:非确定性行为!前一个标签范围内的第一个或最后一个都可以,我认为返回您检查的最后一个可能更容易。
  • 请问为什么需要最里面的元素?我想不出需要这样选择的问题。

标签: javascript jquery html


【解决方案1】:

对于单路径,只需找到没有子节点的元素:

$('body *:not(:has("*"))');

或者,在您更具体的情况下$('#cell0 *:not(:has("*"))');

对于多个路径 - 如果有多个同样嵌套的节点怎么办?此解决方案将为您提供具有最多祖先的所有节点的数组。

var all = $('body *:not(:has("*"))'), maxDepth=0, deepest = []; 
all.each( function(){ 
    var depth = $(this).parents().length||0; 
    if(depth>maxDepth){ 
        deepest = [this]; 
        maxDepth = depth; 
    }
    else if(depth==maxDepth){
        deepest.push(this); 
    }
});

同样,在您的情况下,您可能希望获得表格单元格的最深元素,因此您又回到了单行:

$('#table0 td *:not(:has("*"))');

- 这将返回一个 jQuery 对象,其中包含表中每个单元格的所有最内层子节点。

【讨论】:

    【解决方案2】:

    我会通过一个递归函数来做到这一点:

    // Returns object containing depth and element
    // like this: {depth: 2, element: [object]}
    function findDeepestChild(parent) {
    
        var result = {depth: 0, element: parent};
    
        parent.children().each(
            function(idx) {
                var child = $(this);
                var childResult = findDeepestChild(child);
                if (childResult.depth + 1 > result.depth) {
                    result = {
                        depth: 1 + childResult.depth, 
                        element: childResult.element};
                }
            }
        );
    
        return result;
    }
    

    【讨论】:

      【解决方案3】:

      对于 jQuery,这个问题有一个简单而好的答案。但是,我一直在寻找没有它的优雅解决方案。由于任何浏览器尚不支持:has,因此我在这里提出了这个。

      Array.from( document.querySelectorAll( 'body *' ) )
        .filter( e => !e.children.length );
      

      【讨论】:

        【解决方案4】:

        好吧,从你不知道这个“最深”节点在哪里的基础开始,你可以这样做:

        $.fn.deepest = function() {
          var depth = 0;
          this.find('*').each(function() {
            var d = $(this).parents().length;
            depth = Math.max(d, depth);
          });
          return this.find('*').filter(function() {
            return this.parents().length === depth;
          });
        });
        

        然后

        var $deepest = $('body').deepest();
        

        将(除了我的代码中可能存在的 12 个错误)将是一组最深元素的 jQuery 对象。

        edit - 我的十几个错误之一是这没有考虑起始节点的深度 - 这将是一个弄清楚的技巧。可能会更好地重构它,以便它找到最深的最初选择的列表:

        $.fn.betterDeepest = function() {
          var depth = 0;
          this.each(function() {
            depth = Math.max(depth, $(this).parents().length);
          });
          return this.filter(function() { return $(this).parents().length === depth; });
        });
        

        并在页面上获得最深入的内容:

        var deepest = $('*').betterDeepest();
        

        【讨论】:

          【解决方案5】:

          使用单一路径:

          var elem = $('#table0'),
              next;
          while ((next = elem.children(':first')).length > 0)
            elem = next;
          // elem now points to the deepest child of table0
          

          如果我有时间,我会用多路径的代码更新我的答案。

          【讨论】:

          • 甜蜜。我不知道 next 关键字。
          • @sova - 没有 next 关键字。这里只是一个变量名。
          • @sova: next 不是 JavaScript 中的关键字。由于本网站的语法高亮显示支持多种语言,因此有时会高亮显示其他语言的单词。
          【解决方案6】:

          我要做的第一件事是运行depth first search (DFS) 并跟踪我在 DOM 中的每个节点所处的级别。

          为了避免每次您想要更改某些内容时都必须执行完整的 DFS(假设您想要执行多次),您可以在执行搜索时在树中使用其级别标记每个 DOM 节点. (再说一次,您可能会在生成表的过程中进行此级别标记?)一旦搜索完成并且您已经确定了最深的节点,请保留对该节点的引用。

          从那时起,您应该能够在任何给定时间快速更新处于最深级别的节点。

          【讨论】:

            【解决方案7】:

            也许不是最有效的,但你可以这样做:

            示例: http://jsfiddle.net/5N3Y7/

            var selector = 'body > *';
            var $next = $(selector);
            var $result = $next;
            
            while( $next.length ) {
                $result = $next;
                selector += ' > *';
                $next = $(selector);
            }
            
            $result.css('background','orange');
            

            或者这个:

            示例: http://jsfiddle.net/5N3Y7/2/

            (示例中的结果似乎处于不同级别,但这是因为浏览器正在插入缺少的&lt;tbody&gt;。)

            var $next = $('body');
            
            while ($next.length) {
                $result = $next;
                $next = $result.children();
            }
            
            $result.css('background', 'orange');
            

            【讨论】:

              【解决方案8】:

              它不是特定于 jquery 的,但是可以调整这个递归解决方案来做任何你想做的事情。

              function findDeepest (elem) {
               var result = {maxDepth: 0, deepestElem: null}
               descend(elem, 0, result);
               if (result.maxDepth > 0)
                alert (result.deepestElem.tagName + " " + result.maxDepth);
               }
              
              
              function descend(elem, depth, result) {    
               switch (elem.nodeType) {
                  case 1: // ELEMENT_NODE
                      if (depth > result.maxDepth) {
                       result.maxDepth = depth;
                       result.deepestElem = elem;
                       }
                      for (var i=0; i<elem.childNodes.length; i++)
                         descend(elem.childNodes[i], depth + 1, result);
                      break;
                  case 3: // TEXT_NODE
                  case 4: // CDATA_SECTION_NODE
                  case 5: // ENTITY_REFERENCE_NODE
                  case 8: // COMMENT_NODE
                      // handle these cases as needed
                  break;
                  }
              }
              

              【讨论】:

              • 请解释一下为什么你的switch语句只有一种情况
              • 请注意我关于“正在调整”的评论,在这种情况下,您可能想做其他事情,例如处理文本节点等。诚然,它看起来确实有点奇怪......但我通常使用这种形式来遍历 dom 树。我会在那里发表评论以明确...
              【解决方案9】:

              我认为,这对你来说是最短的示例:

              $('#cell0 :last').text('new information')
              

              真正改变最里面的元素,并保存标签结构

              【讨论】:

                【解决方案10】:

                聚会很晚,但这是我的通用解决方案。

                var elements$ = $(':contains("your text")');
                var result = elements$.filter((index, element) => 
                        index === elements$.length || 
                        ! $.contains(element, elements$.get(index+1))
                    );
                

                它非常快速和简单,因为 jQuery 已经为我们完成了所有工作。

                请注意,elements$ 是容器的有序列表,其中许多包含下一个。因此,最里面的元素是最后一个元素加上不包含下一个元素的所有元素。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-01-30
                  • 2011-10-30
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-01-30
                  • 1970-01-01
                  相关资源
                  最近更新 更多