获取满足两个不同后代选择器标准的“最深”元素

Posted

技术标签:

【中文标题】获取满足两个不同后代选择器标准的“最深”元素【英文标题】:Getting the "deepest" elements that meet two different descendant selector criteria 【发布时间】:2012-07-26 15:02:57 【问题描述】:

我正在编写一个用户脚本,它检测“运送到”任意设计的地址表单并解析其内容。为此,我需要找到包含地址标签(例如“名称”、“地址 1”等)和相应的“行”(可能是也可能不是 tr 元素)的表单input 该标签的字段。比如在下面的sn-p中:

<div>
<label>MaidenName</label>
<table><tbody>
    <tr>
        <td><label>FirstName</label></td>
        <td><input value = "Bob"></td>
    </tr>
    <tr>
        <td><label>LastName</label></td>
        <td><input value = "Smith"></td>
    </tr>
    <tr>
        <td><label>CompanyName</label></td>
        <td><input value = "Ink Inc"></td>
    </tr>
</tbody></table>
</div>

我想匹配所有tr 元素,因为它们每个都包含一个“名称”标签和一个输入字段。但是,由于“MaidenName”标签的原因,我不想匹配 div,因为它的范围比在表内的字段中找到的匹配范围更广。

我当前查找这些行(通常是 div 元素而不是 tr 元素)的算法是:

找到所有带有适当“名称”标签的节点 在每个节点的 DOM 中向上工作,直到找到一个作为输入字段祖先的节点 然后删除我遍历到达那里的子元素,只留下集合的父元素。

从我正在使用的端口翻译,JQuery javascript 将如下所示:

// set up my two lists
var labelNodes = getLabelNodes();
var nodesWithAddress = 
    $().find("input[type='text']:visible, select:visible");

var pathToCommonParents = getLabelNodes()
    .parentsUntil(nodesWithAddressChildren.parents()).parent();

// keep the highest-level nodes, so we only have the common paths - 
//not the nodes between it and the labels.
return combinedNodeSet.filter(
    function (index) return $(this).find(combinedNodeSet).length == 0);

这行得通...但是所有这些遍历和比较开销绝对会破坏我的性能(这可能需要 5 秒 或更长时间。)

什么是更好的实现方式?我认为下面的伪代码会更好,但我可能是错的,我不知道如何实现它:

var filteredSet = $().find(*).hasAnyOf(labelNodes).hasAnyOf(nodesWithAddress);
return filteredSet.hasNoneOf(filteredSet); 

【问题讨论】:

在这种情况下,标签和输入周围是否总是存在类似td 的元素? 总是一些元素,但是环绕元素的嵌套深度和标签名称会有所不同。 【参考方案1】:

更新

我找到了一个更好的解决方案(请参阅修订版)我认为这对您来说非常有效。

function getMySpeacialElements() 
    var aSpeacialElements = [];
    $("label").each(function() 
        var oParent = $(this).parent(),
            oSpeacial = oParent.parent();
        oSpeacial.children("*").each(function() 
            if ($(this).children("input").length) 
                aSpeacialElements.push(oSpeacial[0]);
            
        );
    );
    return aSpeacialElements;

Live DEMO

【讨论】:

谢谢。我敢肯定,如果我坐下来五分钟,我就能弄清楚这是如何工作的,但是未来的 SO 读者(更不用说我自己)可能会从对正在发生的事情的概述中受益。 他查看页面上的每个标签,当它迭代时,标签被设置为父标签。然后他查看所有的孩子并找到那些是输入的孩子。一旦找到输入,他就会将该元素推送到数组中。非常好的方法。【参考方案2】:

概括 micha 的答案,以便它可以以独立于页面的方式(不同的标签和嵌套级别)查看:

var labelNodes = getLabelNodes();
var returnNodes = $();

var regexAncestors = labelNodes.parent();

while (regexAncestors.length)
    regexAncestors = regexAncestors.not("body");
    var commonParentNodes = regexAncestors.has("input[type='text']:visible, select:visible");
    returnNodes.add(commonParentNodes);
    regexAncestors = regexAncestors.not(commonParentNodes).parent();

return returnNodes;

【讨论】:

以上是关于获取满足两个不同后代选择器标准的“最深”元素的主要内容,如果未能解决你的问题,请参考以下文章

css的复合选择器

CSS基础知识二复合选择器元素显示模式背景三大特性注释

CSS系列之后代选择器子选择器和相邻兄弟选择器

后代选择器和子元素选择器的区别

css后代选择器和子选择器的区别

课时73.后代选择器和子元素选择器(理解)