zoukankan      html  css  js  c++  java
  • QWrap Selector解密之四:自选器转化

    QWrap Selector解密之四:自选器转化

    在《认识selector写法》中提到,我们把“没有关系符的选择器”叫“自选器”,例如“input#myId[value=hello]:enabled”,我们先看一下自选器里都有些什么内容,如下表:
    格式 意义
    * 通配类型,特征:以*打头
    E 类型选择,特征:以tagName打头
    #id id选择,特征:“#”号
    .className className选择,特征:“.”号
    [foo][foo="value"][foo~="value"] 属性选择,特征:方括号。支持以下比较关系
    [foo]		isTrue|hasValue
    [foo="value"]		equal
    [foo!="value"]		unequal
    [foo~="value"]		onePart
    [foo|="value"]		firstPart
    [foo^="value"]		beginWith
    [foo$="value"]		endWith
    [foo*="value"]		contains
    :enabled:nth-child(2n) 伪类选择,特征:“:”号。例如以下伪类
    first-child
    last-child
    only-child
    nth-child(nExpr)
    nth-last-child(nExpr)
    first-of-type
    last-of-type
    only-of-type
    nth-of-type(nExpr)
    nth-last-of-type(nExpr)
    empty
    parent
    not(selector)
    enabled
    disabled
    checked
    focus
    indeterminate

    在selector里,自选器经常被用来判断一某节点是否符合自选器,并且会被频繁调用,所以,我们需要高效的自选器判断函数,我们临时命名叫matcher吧。
    以“[value=hello]”为例,来看一下matcher的两种方案。
    方案1:解析input[value=hello],并立即判断
    方案2:解析input[value=hello]成一个函数,以后每次matcher时就调用函数判断。
    方案1的好处是即时解析即时用,不必缓存解析结果;方案2的好处是match多次时,只解析selector一次,将解析出的function多次运行。
    自选器判断会经常用到,我们采用方案2的话,可以将这个解析功能独立出来,让代码清晰很多,并且也能保证效率,所以,QW.Selector提供了一个把自选器转化成过滤函数的方法:QW.Selector.selector2Filter。
     
    /**
    * 把一个selector字符串转化成一个过滤函数.
    * @method selector2Filter
    * @static
    * @param {string} sSelector 过滤selector,这个selector里没有关系运算符(", >+~")
    * @returns {function} : 返回过滤函数。
    * @example:
    var fun=selector2Filter("input.aaa");alert(fun);
    */
    selector2Filter:
    function(sSelector) {
    return s2f(sSelector);
    },
    这个方法除了在selector.js里面用到,在node.h.js里也会用到,例如,NodeH.ancestor(selector)方法里的selector参数就是一个自选器。

    我们还是以自选器“input[value=hello]”为例,看一下它能转化成怎样的过滤函数。
    我们有两种选择:
    方案A:闭包:
    selector2Filter: function(sSelector) {
    //解析selector,得到结果:
    var tagName='INPUT';attrs=[['value','==','hello']];
    return function(el){ //返回判断函数
    if(el.tagName != tagName ) return false;
    for(var i =0;i<attrs.length;i++){
    if(el[attrs[i][0]] != attrs[i][2]) return false;//这里是理论简化极限
    }
    return true;
    }
    }
    方案B:拼字符串,再new Function:
     
    selector2Filter: function(sSelector) {
    //解析selector,得到结果:
    var s="return el.tagName=='INPUT' && el.value=='hello';";
    return new Function('el',s);
    }

    可以看到,拼function方案得出的过滤函数,函数体的构成要简单得多
    事实上,QW.Selector在初期实现时,一直是闭包方式,后来到2010年时,转成了用字符串拼函数体的方式。具体实现参见内部函数s2f:
    View Code
    /*
    * s2f(sSelector): 由一个selector得到一个过滤函数filter,这个selector里没有关系运算符(", >+~")
    */
    var filterCache = {};

    function s2f(sSelector, isForArray) {
    if (!isForArray && filterCache[sSelector]) return filterCache[sSelector];
    var pseudos = [],
    //伪类数组,每一个元素都是数组,依次为:伪类名/伪类值
    s = trim(sSelector),
    reg
    = /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/g,
    //属性选择表达式解析,thanks JQuery
    sFun = [];
    s
    = s.replace(/\:([\w\-]+)(\(([^)]+)\))?/g, //伪类
    function(a, b, c, d, e) {
    pseudos.push([b, d]);
    return "";
    }).replace(
    /^\*/g,
    function(a) { //任意tagName缩略写法
    sFun.push('el.nodeType==1');
    return '';
    }).replace(
    /^([\w\-]+)/g,//tagName缩略写法
    function(a) {
    sFun.push(
    'el.tagName=="' + a.toUpperCase() + '"');
    return '';
    }).replace(
    /([\[(].*)|#([\w\-]+)|\.([\w\-]+)/g,//id缩略写法//className缩略写法
    function(a, b, c, d) {
    return b || c && '[id="' + c + '"]' || d && '[className~="' + d + '"]';
    }).replace(reg,
    //普通写法[foo][foo=""][foo~=""]等
    function(a, b, c, d, e) {
    var attrGetter = Selector._attrGetters[b] || 'el.getAttribute("' + b + '")';
    sFun.push(Selector._operators[c
    || ''].replace(/aa/g, attrGetter).replace(/vv/g, e || ''));
    return '';
    });
    if (!(/^\s*$/).test(s)) {
    throw "Unsupported Selector:\n" + sSelector + "\n-" + s;
    }
    for (var i = 0, pI; pI = pseudos[i]; i++) { //伪类过滤
    if (!Selector._pseudos[pI[0]]) throw "Unsupported Selector:\n" + pI[0] + "\n" + s;
    if (/^(nth-|not|contains)/.test(pI[0])) {
    sFun.push(
    '__SltPsds["' + pI[0] + '"](el,"' + encode4Js(pI[1]) + '")');
    }
    else {
    sFun.push(
    '__SltPsds["' + pI[0] + '"](el)');
    }
    }
    if (sFun.length) {
    if (isForArray) {
    return new Function('els', 'var els2=[];for(var i=0,el;el=els[i++];){if(' + sFun.join('&&') + ') els2.push(el);} return els2;');
    }
    else {
    return (filterCache[sSelector] = new Function('el', 'return ' + sFun.join('&&') + ';'));
    }
    }
    else {
    if (isForArray) {
    return function(els) {
    return arrFilter(els, retTrue);
    };
    }
    else {
    return (filterCache[sSelector] = retTrue);
    }

    }
    }

    另外,在简版selector_sapmple.js中,还注释了一个更简版的只支持*、tagName、#id、.className四种选择器的自选器转换函数:
     
    View Code
    /*
    备用代码,更简版s2f
    function s2f(sSelector){
    var attrs=[];//属性数组,每一个元素都是数组,依次为:属性名/属性比较符/比较值
    var s=sSelector;
    var shorthands=[
    [/\#([\w\-]+)/g,function(a,b){attrs.push('el.id=="'+b+'"');return '';}],//id过滤
    [/^\*+/g,function(a,b){attrs.push('el.tagName');return '';}],//Element过滤
    [/^([\w\-]+)/g,function(a,b){attrs.push('el.tagName=="'+b.toUpperCase()+'"');return '';}],//tagName过滤
    [/\.([\w\-]+)/g,function(a,b){attrs.push('el.className && (" "+el.className+" ").indexOf(" '+b+' ")>-1');return '';}]//className过滤
    ];
    for(var i=0,sh;sh=shorthands[i];i++){
    s=s.replace(sh[0],sh[1]);
    }
    if(s) throw ("Unsupported Selector:\n"+sSelector+"\n"+s);
    if(attrs.length){
    return new Function('el','return '+attrs.join('&&'));
    }
    return function(el){return true;};
    };
    */



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

  • 相关阅读:
    查看文件(或文件夹)被哪个进程使用【文件已在另一程序中打开】
    MinGW32和64位交叉编译环境的安装和使用
    MinGW下编译openssl, json-c
    曲演杂坛--SQLCMD下执行命令失败但没有任何错误提示的坑
    Python2.6下基于rsa的加密解密
    MySQL--使用xtrabackup进行备份还原
    Python--过滤Mysql慢日志
    MySQL--将MySQL数据导入到SQL Server
    Python--命令行参数解析Demo
    python--同一mysql数据库下批量迁移数据
  • 原文地址:https://www.cnblogs.com/jkisjk/p/qwrap_selector_selector2filter.html
Copyright © 2011-2022 走看看