Javascript 在大型DOM中使用document.querySelector更快地搜索元素

Javascript 在大型DOM中使用document.querySelector更快地搜索元素,javascript,dom,css-selectors,casperjs,selectors-api,Javascript,Dom,Css Selectors,Casperjs,Selectors Api,在一个包含数百个元素的巨大DOM中,使用document.querySelector(“input[name='foo'][value='bar'])查找每个元素大约需要3-5秒。有没有办法减少这次的工作量?可以通过提供元素的完整路径,例如,document.querySelector(“父-子-孙等”,然后input[name='foo'][value='Modem'])或其他方式 我正在使用CasperJS测试一个大型网页,获取每个元素需要很长时间,这使得我的测试运行了一个小时。。我还尝试了

在一个包含数百个元素的巨大DOM中,使用
document.querySelector(“input[name='foo'][value='bar'])
查找每个元素大约需要3-5秒。有没有办法减少这次的工作量?可以通过提供元素的完整路径,例如,
document.querySelector(“父-子-孙等”
,然后
input[name='foo'][value='Modem'])
或其他方式

我正在使用CasperJS测试一个大型网页,获取每个元素需要很长时间,这使得我的测试运行了一个小时。。我还尝试了
\uuuutils\uuuu.findOne()
,但每个元素的结果都是相同的3-4秒。由于我的测试集中在整个页面的一小部分,我希望有什么方法可以告诉
document.querySelector
将元素搜索集中在页面的特定部分

那么,有人能告诉我从大型DOM中获取元素的最快方法是什么吗

更新:这是我测量时间的方式

var init = (new Date()).getTime();
  var element=this.evaluate(function() {
        return document.querySelector('input[value='somethin'][name='somethin']');
    });
  this.echo('Time Taken :'+((new Date()).getTime() - init));
不知何故,当我从表单中获取单选按钮、选择元素和文本框时,时间非常长,但在几毫秒内返回(我今天才注意到这一点)

在chrome等现代浏览器控制台中运行
document.querySelector('input[value='somethin']][name='somethin']')
时,时间不到一秒钟

我不知道这是否与phantomjs的无头浏览器有关。只有对于该网站中的特定页面,获取元素的速度才会减慢

是的,页面非常大,包含数十万个元素。这是一款已有十年历史的传统网络应用程序。在IE 8的页面上,按F12键查看源代码会使IE挂起5分钟,但不是chrome或firefox。可能是phantomjs的内存过载或其他原因,当我在特定页面上运行测试时,phantomjs很少崩溃。我不知道这些信息是否有用,但我不确定什么是相关的

一般考虑 最快的选择器将是id选择器,但即使您的id位于树的更高位置,它们也不会给您带来太多好处。正如Ian在中指出的,选择器从右到左进行分析/计算。这意味着引擎将查找所有具有匹配属性的输入,即使它只有一个,然后搜索树以查看前面的元素是否匹配

我发现,如果您知道输入在哪个封闭元素中,那么可以使用JavaScript DOM属性遍历DOM,并在树的一小部分上运行
querySelector
。至少在我的测试中,这减少了一半以上的时间

记忆问题 从你最新的问题来看,这似乎真的是一个记忆问题。当您拥有数十万个元素时,相对较旧的PhantomJS WebKit引擎将尝试分配足够的内存。当它占用的内存超过可用内存或甚至超过您的机器所拥有的内存时,操作系统会使用硬盘上的交换内存进行补偿

当脚本尝试查询当前仅处于交换状态的元素时,此查询需要很长时间,因为它必须从高延迟硬盘中获取数据,这与内存相比非常慢

我的测试针对100k个表单运行,每个查询的每个元素不到30毫秒。当我增加元素的数量时,执行时间线性增长,直到在某个点我得到(by)

我的机器上的输出(更好):

cssbyForm:29.55 cssbyFormChild:29.97 cssbyFormChildHybridChild:11.51 cssbyFormHybridChild:10.17 cssbyFormJsDomChild:11.73 cssplain:29.39 xpathbyForm:206.66 xpathplain:207.05
注意:我直接使用了PhantomJS。如果在CasperJS中使用相同的技术,则不会产生不同的结果。

如果可以使用ID或类,可能会更快。浏览器通常对此进行了优化,因为它们在CSS中使用得非常频繁。重要的是,选择器应该从可以在不搜索整个DOM的情况下找到的内容开始。这些选择器从右到左进行解析,因此,向选择器中添加
父子孙等可能会减慢它的速度,因为它必须确保其祖先是正确的,而不仅仅是标记名、名称和值。调用
查询选择器
需要3-5秒,这是不可信的。你是怎么测量的?即使是在一个大型文档和复杂查询中,也要花费几十毫秒以上的时间,我会感到惊讶。在任何情况下,您都可以调用
element.querySelector
,它会将搜索限制在该元素内。您是否可以确认DOM中有“数百个元素”,看起来并没有那么大……这个答案无法解释可能导致这种荒谬性能的原因(每个查询3-5秒)OP报告。你的实验很有趣,但我们谈论的是毫秒。你说得对,我应该问一下,OP是如何测量3-5秒的。起初我认为这可能发生,但在我自己的测试和100k元素以及每次查询不到30毫秒的情况下,我怀疑这是一个查询问题,而是一些不必要的
等待
之类的问题。另一种可能是DOM太大,无法装入内存,操作系统正忙于交换。我在尝试20万个表单时遇到了这个问题;他是说“数十万”吗?当然,在这种情况下,我可以想象事情会急剧放缓。是的,你的
等待
假设听起来很有道理。或者他可能在每次测试中重新加载页面,3-5秒是页面加载时间? runtime error R6016 - not enough space for thread data
var page = require('webpage').create();
var content = "",
    max = 100000,
    i;

for(i = 0; i < max; i++) {
    content += '<form id="f' + i + '"><input type="hidden" name="in' + i + '" valuate"iv' + i + '"></form>';
}

page.evaluate(function(content){
    document.body.innerHTML = content;
}, content);

console.log("FORMS ADDED");

setTimeout(function(){
    var times = page.evaluate(function(max){
        var obj = {
            cssplain: 0,
            cssbyForm: 0,
            cssbyFormChild: 0,
            cssbyFormJsDomChild: 0,
            cssbyFormChildHybridChild: 0,
            cssbyFormHybridChild: 0,
            xpathplain: 0,
            xpathbyForm: 0
        },
            idx, start, el, i,
            repeat = 100;

        function runTest(name, obj, test) {
            var idx = Math.floor(Math.random()*max);
            var start = (new Date()).getTime();
            var el = test(idx);
            obj[name] += (new Date()).getTime() - start;
            return el;
        }

        for(i = 0; i < repeat; i++){
            runTest('cssplain', obj, function(idx){
                return document.querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyForm', obj, function(idx){
                return document.querySelector('#f'+idx+' input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormChild', obj, function(idx){
                return document.querySelector('form:nth-child('+(idx+1)+') input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormJsDomChild', obj, function(idx){
                return document.body.children[max-1].querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormChildHybridChild', obj, function(idx){
                return document.querySelector('form:nth-child('+(idx+1)+')').querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormHybridChild', obj, function(idx){
                return document.querySelector('#f'+idx).querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('xpathplain', obj, function(idx){
                return document.evaluate('//input[@name="in'+idx+'" and @value="iv'+idx+'"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            });

            runTest('xpathbyForm', obj, function(idx){
                return document.evaluate('//form[@id="f'+idx+'"]//input[@name="in'+idx+'" and @value="iv'+idx+'"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            });
        }
        for(var type in obj) {
            obj[type] /= repeat;
        }
        return obj;
    }, max);
    console.log("TIMES");
    for(var type in times) {
        console.log(type+":\t"+times[type]);
    }
    phantom.exit();
}, 0); // just in case the content is not yet evaluated
cssbyForm: 29.55 cssbyFormChild: 29.97 cssbyFormChildHybridChild: 11.51 cssbyFormHybridChild: 10.17 cssbyFormJsDomChild: 11.73 cssplain: 29.39 xpathbyForm: 206.66 xpathplain: 207.05