• QWrap Selector解密之二:从左向右,还是从右向左


    QWrap Selector解密之二:从左向右,还是从右向左

    关于Selector实现,问得最多的问题是:是从左往右,还是从右往左。

    先看一下它们有什么不同,以Selector.query('div span',document.body)为例。看下表:
      从左往右 从右往左
    策略简介 先query得到divs,
    再通过divs来query得到spans
    先query得到spans,再通过是否有父节点是div来过滤
    问题 思路简单,但除重与排序麻烦 过滤麻烦,但不用考虑除重与排序
    解决方案 如果整个selector里只有后代关系符与亲子关系符,
    可以在通过divs来query spans之前,
    对divs进行一次过滤(如果前一个包含后一个,则过滤掉后一个),
    来避免得到spans后还需要排序。
    寻路径过滤时可以使用一些临时缓存策略来提速

    事实上,我08年实现的第一版Selector,采用的是从左往右的策略,为很多种情况的query进行了除重与排序的优化,可是始终无法做到理论的完美,特别是关系符很复杂时,例如:“div p~ul li”。在Selector正式使用于项目之前,我终于痛下决心,放弃了从左往右的策略,而采用一种保证理论严谨同时又保证效率的策略。
    这策略专门作为注释写进了Selector的代码里:
    /*
    为了提高查询速度,有以下优先原则:
    最优先:原生查询
    次优先:在' '、'>'关系符出现前,优先正向(从左到右)查询
    次优先:id查询
    次优先:只有一个关系符,则直接查询
    最原始策略,采用关系判断,即:从最底层向最上层连线,能连线成功,则满足条件
    */
    也就是说,它即不是单纯的从左往右,也不是单纯的从右往左
    其中,原生查询是在浏览器支持querySelectorAll,并且selector不会有浏览器差异的情况下才采用。
    分析一下剩下的几条策略,我们把它编一下号:
        一次优先:在' '、'>'关系符出现前,优先正向(从左到右)查询
        二次优先:id查询
        三次优先:只有一个关系符,则直接查询
        最原始策略,采用关系判断,即:从最底层向最上层连线,能连线成功,则满足条件


    通过下表一些选择器例子来理会。
    序号 selector 处理顺序
    1 refEl.query('div.aaa') 不符一次优先,不符二次优先,符合三次优先,所以,
    return refEl.getElements('div.aaa');
    2 refEl.query('>div.aaa') 不符一次优先,不符二次优先,符合三次优先,所以,
    return refEl.getChildren('div.aaa');
    3 refEl.query('+div.aaa') 符合一次优先,所以,
    return refEl.getNext('div.aaa');
    4 refEl.query('~div.aaa') 符合一次优先,所以,
    return refEl.getNexts('div.aaa');
    5 refEl.query('div.aaa span.bbb') 不符一次优先,不符二次优先,不符三次优先,用最原始策略,所以,
    return refEl.getChildren('span.bbb').filter('div.aaa',refEl);
    6 refEl.query('>div.aaa span.bbb') 不符一次优先,不符二次优先,不符三次优先,用最原始策略,所以,
    return refEl.getChildren('span.bbb').filter('>div.aaa',refEl);
    7 refEl.query('+div.aaa span.bbb') 符合一次优先,所以,先用第一次优先原则切除左边的第一个关系符与自选器,
    return refEl.getNext('div.aaa').query(' span.bbb');
    即:
    return refEl.getNext('div.aaa').getElements('span.bbb');
    注:对于本例子的处理,jquery的策略会bug,
    参见:http://www.cnblogs.com/rubylouvre/archive/2011/01/24/1942818.html#2018130
    8 refEl.query('~div.aaa span.bbb') 符合一次优先,所以,先用第一次优先原则切除左边的第一个关系符与自选器,
    return refEl.getNexts('div.aaa').query(' span.bbb');
    即:
    return refEl.getNexts('div.aaa').getElements('span.bbb');
    注:对于本例子的处理,jquery的策略会bug,
    参见:http://www.cnblogs.com/rubylouvre/archive/2011/01/24/1942818.html#2018130
    9 refEl.query('div div.aaa span.bbb') 不符一次优先,不符二次优先,不符三次优先,用最原始策略,所以,
    refEl.getChildren('span.bbb').filter('div div.aaa',refEl)。
    10 refEl.query('+div.aaa+ul span.bbb') 符合一次优先,所以,先用第一次优先原则切除左边的第一个关系符与自选器,
    return refEl.getNext('div.aaa').query('+ul span.bbb');
    切后的右半部分,变成了例7。
    11 refEl.query('~div.aaa+ul span.bbb') 符合一次优先,所以,先用第一次优先原则切除左边的第一个关系符与自选器,
    return refEl.getNexts('div.aaa').query('+ul span.bbb');
    切后的右半部分,变成了例7。 注:本例存在结果中有重复元素的可能。。。有人能说出为什么吗?
    12 refEl.query('~div.aaa~ul span.bbb') 符合一次优先,所以,先用第一次优先原则切除左边的第一个关系符与自选器,
    return refEl.getNexts('div.aaa').query('~ul span.bbb');
    不过,这样做明显有重复,需要需要在第二次query之前,只保留一个:
    return refEl.getNexts('div.aaa')[0].query('~ul span.bbb');
    切后的右半部分,变成了例8。
    13 refEl.query('div#id') 不符一次优先,符合二次优先,所以,
    return refEl.getElementById('id').filter('div#id',refEl);
    14 refEl.query('div div#id') 不符一次优先,符合二次优先,所以,
    return refEl.getElementById('id').filter('div div#id',refEl);
    15 refEl.query('div div#id span.bbb') 不符一次优先,符合二次优先,所以,
    return refEl.getElementById('id').filter('div div#id',refEl).query('span.bbb');
    注:这一个是从中间查的典型例子,通过#id所在的自选器,将selector分成两步分。
    16 refEl.query('~div div#id') 符合一次优先,所以,
    return refEl.getNexts('div').query('div#id');
    注:虽说refEl.getNexts('div')是多个,但refEl.getNexts('div').query('div#id');还是最多只有一个。
    17 .... ....



    从上面的诸多例子可以看到,除了极少数的情况会有重复结果的可能之外,绝大多数选择器并不需要排序与除重。

    另外,在filter方法中,查找路径从是从右往左找路径的。上面的诸多例子里使用的filter时的参数selector,都不是以“+”或“~”开头的。
    QWrap偷了下懒,filter的selector参数,不支持以“+”或“~”,这个算是一个理论的缺憾。。。不过ms只是理论问题,实际中很少碰到,碰到的话请换方法解决。

    另:与本文所提到的方向有关的代码,主要集中在私有方法querySimple里

    /*
    * querySimple(pEl,sSelector): 得到以pEl为参考,符合过滤条件的HTML Elements.
    * @param {Element} pEl 参考元素
    * @param {string} sSelector 里没有","运算符
    * @see: query。
    */

    function querySimple(pEl, sSelector) {
    querySimpleStamp
    ++;
    /*
    为了提高查询速度,有以下优先原则:
    最优先:原生查询
    次优先:在' '、'>'关系符出现前,优先正向(从左到右)查询
    次优先:id查询
    次优先:只有一个关系符,则直接查询
    最原始策略,采用关系判断,即:从最底层向最上层连线,能连线成功,则满足条件
    */
    ...
    }

    附QWrap网址:http://www.qwrap.com

  • 相关阅读:
    Adapter 适配器模式
    词法分析器的作用
    文法、语言、正则表达式
    基于Windows的套接字相关函数及示例
    迭代器和生成器
    反射的使用
    hasattr、getattr、setattr反射
    socket建立tcp、udp链接
    异常处理
    描述符——类型检测,待补充……
  • 原文地址:https://www.cnblogs.com/jkisjk/p/qwrap_selector_direction.html
Copyright © 2020-2023  润新知