jQuery.closest() 实现的 DOM/纯 JavaScript 解决方案?

IT技术 javascript dom
2021-03-07 05:13:19

这是我要查询的标记。所以给定标记:

<table class="non-unique-identifier table">
<tr><td><div id="unique-identifier"></div></td></tr>
</table>

我正在查询#unique-identifier:

var myDiv = document.getElementById('#unique-identifier');

然后我试图选择表格。问题是我想让代码不脆弱,所以我不需要这样做:

var myDiv = document.getElementById('#unique-identifier'),
    myTable = myDiv.parentNode.parentNode.parentNode.parentNode;

我的问题

当前是否有等效于 $().closest() 的 jQuery 的 DOM 实现?没有嵌套 for 循环的最接近的实现将是首选。

限制

我被要求不要针对这个特定问题使用 jQuery 或 sizzle 或引入任何新库。代码也很旧。因此,这就是这种限制和存在的原因<tables>

6个回答

没有循环你不能这样做:

function closest (el, predicate) {
  do if (predicate(el)) return el;
  while (el = el && el.parentNode);
}

嗯,实际上你可以,使用递归(一个伪装的循环):

function closest(el, predicate) {
  return predicate(el) ? el : (
     el && closest(el.parentNode, predicate)
  );
}

演示(使用Sizzle进行 DOM 查询):

// s = selectors
// n = number of selectors
// get closest s[i+1] from s[i]
// where 0 <= i < n and i % 2 = 0

function main (s) {
  var i, el, from;
  var n = s.length;
  for (i = 0; i < n; i += 2) {
    from = Sizzle(s[i])[0];
    el = closest(from, function (el) {
      return !!el && el !== document && (
        Sizzle.matchesSelector(el, s[i + 1])
      );
    });
    console.log(el);
  }
}

function closest (el, predicate) {
  do if (predicate(el)) return el;
  while (el = el && el.parentNode);
}

main([
  "#winner" , "b", 
  "#winner" , "p", 
  "#winner" , "div", 
  "#winner" , "div:not(#trump)", 
  "#winner" , "#clinton",
  "#looser" , "html"
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/sizzle/1.10.18/sizzle.min.js"></script>

<div id="main">
  <div id="trump">
    <p>Donald <b id="winner">Trump</b></p>
  </div>
  <div id="clinton">
    <p>Hillary <b>Clinton</b></p>
  </div>
</div>

return el.classList.contains('item') 如果有多个 classNames 会更好。
2021-04-26 05:13:19

要添加更新的答案,现在Element.closest(<query_selector>)可用。

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

这在 IE 上不受支持,但该 mozilla 文档页面包含用于 IE8 和 IE9+ 的 polyfill 的代码。

尽管 5 年前当 OP 询问时它不可用,但现在几乎每个浏览器都提供本机解决方案(其余浏览器使用标准 polyfill),这绝对应该是任何实际实现的首选方法,尽管其他出于学术/教育目的,答案很有趣。
2021-04-29 05:13:19
现在这个解决方案是最好的方法...... jquery 弃用迫在眉睫
2021-05-21 05:13:19

通过任何 css 选择器搜索最接近元素的简洁快速(使用 Benchmark.js 测试)方法:

var ep = Element.prototype;
ep.matches = ep.matches || ep.webkitMatchesSelector || ep.msMatchesSelector || ep.mozMatchesSelector;

function getClosest( elem, selector ) {
    while (elem !== document.body) {
        elem = elem.parentElement;
        if (elem.matches(selector)) return elem;
    }
}

支持 IE9+ 和其他您可能关心的浏览器。

function closestById(el, id) {
  while (el.id != id) {
    el = el.parentNode;
    if (!el) {
      return null;
    }
  }
  return el;
}

// Use it like:

yourTarget = closestById(document.getElementById('unique-identifier'),'targetId')
alert(yourTarget.id);
<div id="targetId">
  Finish
  <div>
    <div id="unique-identifier">
      Start
    </div>
  </div>
</div>

这将向上搜索,直到找到某个 ID。您还可以更改代码以查找某些类。

另一种是递归函数。当我搜索孩子时,这与最接近的略有不同,我不确定是否最接近。

function closest(elem) {
    if( elem.className.indexOf("non-unique-identifier") ) {
        return elem;
    } 

    var parent = elem.parentNode;

    for(var i = 0; i< parent.children.length; i++ ) {
        if( parent.children[i].className.indexOf("non-unique-identifier")!=-1)  {
            return parent.children[i];
        }
    }

    return closest(parent);
}



var elem = document.getElementById('unique-identifier');

var cl = closest(elem);

console.log(cl);

非儿童搜索示例(更像是最接近的):

function closest(elem) {
    if( elem.className.indexOf("non-unique-identifier") ) {
        return elem;
    } 

    var parent = elem.parentNode;

    if( parent.className.indexOf("non-unique-identifier")!=-1) {
        return parent;
    }    

    return closest(parent);
}



var elem = document.getElementById('unique-identifier');

var cl = closest(elem);

console.log(cl);