【问题标题】:Check event.target.parentElement with matchesSelector js使用 matchSelector js 检查 event.target.parentElement
【发布时间】:2012-10-19 15:34:50
【问题描述】:

不关心旧的浏览器回退。另外,不能使用库。

我有一个事件对象。我正在通过matchesSelector针对css选择器测试event.target:

event['target'].matchesSelector('css selector here');

这很有效:

event['target']['parentElement'].matchesSelector('css selector here');

...和:

event['target']['parentElement']['parentElement'].matchesSelector('css selector here');

我正在寻找的是一些超出我理解的可能的对象方法,我可以使用它来一直检查每个 parentElement 是否匹配,而无需 for 循环。我的重点是效率。

谢谢!

【问题讨论】:

  • 只是一个想法......也许将 event.target 的父母“扁平化”为某种可以查询的对象?
  • 为什么要避免for 循环? while 循环也可以工作,或者可能是递归,但我不确定你为什么要这样做。
  • @pimvdb 为了获得绝对的最佳效率,一次访问和匹配事件对象属性的“浏览器原生”方式
  • 技术上,在节点列表上使用document.querySelectorAll('...') 然后indexOf 不会每次都需要matchesSelector。但我怀疑它会更快。 matchesSelector 只在元素上定义,而不是节点列表,所以你不能完全避免某种循环。

标签: javascript events match


【解决方案1】:

closest() 函数将满足您的需求。

它从元素本身开始,遍历父元素(朝向文档根),直到找到与提供的选择器字符串匹配的节点。有点类似于 jQuery 的 parents() 函数。

所以您的代码将如下所示:

event.target.closest(selectorString)

【讨论】:

  • 哇,这正是人们应该使用的。干得好!
【解决方案2】:

为防止重复遍历目标元素的所有父元素,您可以使用 matchesSelector() 和选择器(连接原始选择器并附加)来快速检查您的元素是否在与选择器匹配的元素内由空格和目标元素的标签名称组成的上下文选择器:

function getAncestorBySelector(elem, selector) {
    if (!elem.matchesSelector(selector + ' ' + elem.tagName)) {
        // If element is not inside needed element, returning immediately.
        return null;
    }

    // Loop for finding an ancestor element that matches your selector.
}

【讨论】:

【解决方案3】:

Marat Tanalin 的解决方案很好,但elem.matchesSelector 的标准语法是elem.matches

function getAncestorBySelector(elem, selector) {
    if (!elem.matches(selector + ' ' + elem.tagName)) {
        // If element is not inside needed element, returning immediately.
        return null;
    }

    // Loop for finding an ancestor element that matches your selector.
}

不幸的是,并非所有浏览器都支持这种语法。一些浏览器仍然实现了 elem.matchesSelector 的前缀版本,而 Opera Mini 根本不支持这个功能。

为了缓解这个问题,您可以将描述的方法与以下polyfill 结合起来,如suggested by the MDN

if (!Element.prototype.matches) {
    Element.prototype.matches = 
        Element.prototype.matchesSelector || 
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector || 
        Element.prototype.oMatchesSelector || 
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;            
        };
}

或者,您可能想考虑完全抛弃elem.matchesSelector 并采用这样的方式:

function getAncestorBySelector(elem, selector) {
    if([].indexOf.call(document.querySelectorAll(selector + ' ' + elem.tagName), elem) === -1) {
        // If element is not inside needed element, returning immediately.
        return null;
    }
    // Loop for finding an ancestor element that matches your selector.
}

两种实现都需要至少支持querySelectorAll,这是所有现代浏览器都支持的功能。

浏览器支持:

【讨论】:

  • 请注意,使用 Mozilla 提供的 Polyfill 可以轻松地使 element.matches 跨浏览器兼容。看我上面的评论
  • @Pancho :这确实是一种合理的方法。我更新了我的答案,将您的建议作为替代实施。
【解决方案4】:

大多数要求在 vanilla-js 中重新实现 jQuery 的 parents() 方法的问题都作为这个问题的重复而关闭,我想提供一个更现代的示例来更好地模拟 jQuery 的行为。

打字稿:

function getAllParentElements(child: HTMLElement, selector: string = '*') {
    const parents: HTMLElement[] = [];

    let parent: HTMLElement = child.parentElement?.closest(selector);
    while (parent) {
        parents.push(parent);
        parent = parent.parentElement?.closest(selector);
    }

    return parents;
}

Javascript:

function getAllParentElements(child, selector = '*') {
    const parents = [];

    let parent = child.parentElement && child.parentElement.closest(selector);
    while (parent) {
        parents.push(parent);
        parent = parent.parentElement && parent.parentElement.closest(selector);
    }

    return parents;
}
getAllParentElements(childEl); // -> return all ancestors including body & html elements
getAllParentElements(childEl, '.foo'); // -> return all ancestors with `.foo` class

【讨论】:

    猜你喜欢
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多