• 【你的jQuery?是你的jQuery】(三)——jQuery的选择器


    目的:

    了解jQuery实例对象的组成方式,并打造出形如 $(".class tag #id") 这种路径组合似的查找

    解析:

    上代码之前,先介绍一下$(oo)或者$(xx)什么的,其实就是一个含有length属性的类数组,其中包含的是符合规则的DOM元素,通过$(oo)[0]、$(oo)[1]、$(oo)[2]……这种形式,但又不是数组,因为没有Array的原型方法(push,slice等)。

    所以我们

    1   var jQuery = window.jQuery = window.$ = function (selector, context) {
    2         return new jQuery.fn.init(selector, context);
    3   }

    new出来的是一个类数组,也就是实例对象的组成方式。在调用.css()、.attr()等方法时候,也就是遍历这个类数组,然后分别操作。

    所以在介绍选择器前我们先了解两个静态方法:

    一、$.each( )

    jQuery中有一个静态方法是$.each( ),它可以遍历对象或者数组进行callback

     1  jQuery.extend({
     2      each: function (object, callback) {
     3             var isObject = Object.prototype.toString.apply(object)=="[object Object]", i = 0, length = object.length;
     4             if (isObject && !length) { //obj
     5                 for (name in object)
     6                     if (callback.call(object[name], name, object[name]) === false)
     7                         break;
     8             }
     9             else {  //array
    10                 for (var value = object[0]; i < length && callback.call(value, i, value) !== false; value = object[++i]) { }
    11             }
    12             return this;
    13         }
    14 
    15 })

    说明:

    此方法参照jQuery源码,只是给出了遍历时候,没有额外参数的情况。此方法支持形如$.each(obj,callback(key,vlaue){...})和$.each(array,callback(key,vlaue){...})这两种情况,即遍历对象和数组。

    1、采用Object的原型方法toString,此方法可以判断出任何数据类型,包括null、undefined等,建议用此方法,因为typeof有的数据类型判断不准,例如typeof [] 和typeof {}结果都为“object”

    2、循环的中断条件为callback中,你设置了返回fasle

    3、第二个for写的实在是浪啊... ,执行语句也在了条件语句里。拿出来就能理解了:

    1 for (var value = object[0]; i < length ;i++) {
    2   if(callback.call(value, i, value) === false){
    3       break;
    4   }
    5    value = object[i];
    6  }

    二、$.getElementByClassName( )

    因为IE等老一辈浏览器不支持按类查找(不含API:querySelectorAll),属于兼容处理,自己写一个

     1 jQuery.extend({
     2     getElementByClassName: function (selector, context) {
     3             var ret = [], context = context || document;
     4             if (document.querySelectorAll) {
     5                 ret = context.querySelectorAll(selector);
     6             }
     7             else {
     8                 var elems = context.getElementsByTagName("*"),
     9                 className = document.all ? "className" : "class",
    10                 selector = selector.replace(".", ""),
    11                 reg = new RegExp("\\b" + selector + "\\b");
    12                 for (var i = 0, len = elems.length; i < len; i++) {
    13                     if (elems[i].nodeType == 1 && reg.test(elems[i].getAttribute(className))) {
    14                         ret.push(elems[i]);
    15                     }
    16                 }
    17             }
    18             return ret;
    19         }
    20 })

    说明:

    此方法,很简单,是遍历DOM树,正则判断是否其样式类名为选择类名,符合要求压入数组,最后一并返回。

    好,至此,选择器前的准备工作就剩一个了,了解Sizzle (jQuery的选择器引擎) 的思路组成。

    思路:也就是“从右至左”的查找,形如 $(".class1 .class2 .class3"),正常思路是查找 .class1下的所有 .class2,然后再查找 .class3这样进行了3次DOM查找,而换成“从右至左”也就是查找所有的.class3,然后得到的节点遍历其父节点,符合.class2 的节点再遍历判断是否其父节点是否符合.class1,这样只遍历一遍DOM,遍历节点属性,要比遍历DOM要节省的多。

    组成选择器(select)过滤器(filter),即实现上述思路中的查找和判断。

    三、原型方法 init

     1 jQuery.fn = jQuery.prototype = {
     2             init: function (selector, context) {
     3                 var elems, _this = this;
     4                 this.contexts = []; //上下文,用于存放符合条件节点的上下文,为以后若再次查找留个依据,例如$(oo).find(xx),find时候用
     5                 if (Object.prototype.toString.apply(selector)) { //str情况
     6                     selector = selector.replace(/^\s+|\s+$/,"");//出去选择字符串前后空格
     7                     elems = this.select(selector, context); //返回符合要求的节点
     8                     if (elems[0]) {  //如果此类型节点存在的话
     9                         jQuery.each(elems, function (k, v) {//遍历构造出this[0],this[1]...这种类数组
    10                             _this.contexts[k] = v;
    11                             _this[k] = v;
    12                         })
    13                         this.length = elems.length;
    14                         this.selector = selector;
    15                         return this;
    16                     }
    17                 }
    18             }

    说明:

    此篇仅考虑传入正确字符串的情况,其中还有传入节点、函数、节点数组、类数组(jQuery实例)等情况,后续篇会进行添加。

    为了方便理解,说明部分至于代码注释处。

    四、原型方法 select

     1 jQuery.fn = jQuery.prototype = {
     2             select: function (selector, context) {
     3                 var aQuery = selector.split(/\s+/), context = context || document, match = [], elem, result, ret = [], _this = this;
     4                 match = _reg.match_id_class_tag.exec(aQuery[aQuery.length - 1]);
     5                 if (match[1] == "#") { //选择#id
     6                     elem = [context.getElementById(match[2])];
     7                 }
     8                 else if (match[1] == ".") { //选择.class
     9                     elem = jQuery.getElementByClassName(match[0], context);
    10                 }
    11                 else if (!match[1]) {  //选择 tag
    12                     elem = context.getElementsByTagName(match[2]);
    13                 }
    14                 //分割线----------------------------------------------
    15                 if (elem[0]) {
    16                     jQuery.each(elem, function (k, v) {
    17                         result = _this.filter(v, aQuery, context); //遍历过滤
    18                         if (result != null) {
    19                             ret.push(result);
    20                         }
    21                     })
    22                 }
    23                 return ret;
    24             }
    25         }

    说明:

    1、分割线以上:按照上述思路,将传入参数处理,直接选取最后一个参数,选择符合其要求的节点

    2、分割线一下:进行过滤操作,将符合要求的节点压入数组

    五、原型方法 filter

     1 jQuery.fn = jQuery.prototype = {
     2        filter: function (elem, aQuery, context) {
     3        var match = [],isMatch = [],i = aQuery.length - 1,context = context || document.body,result,parentNode,matchParentNode=elem;
     4                 if (i == 0) { return elem; } //如果就一个选择条件
     5                 parentNode = elem.parentNode;
     6                 while (i--) {//倒序循环选择条件
     7                     match = /(#|\.)?(\w+)/.exec(aQuery[i]); //匹配id class tag
     8                     while (parentNode != context) {
     9                         if (match[1] == "#") { //选择#id
    10                             if (parentNode.getAttribute("id") == match[2]) {
    11                                 isMatch[i] = true;
    12                                 matchParentNode = parentNode;
    13                             }
    14                         }
    15                         else if (match[1] == ".") { //选择.class
    16                             var className = document.all ? "className" : "class";
    17                             if (parentNode.getAttribute(className) == match[2]) {
    18                                 isMatch[i] = true;
    19                                 matchParentNode = parentNode;
    20                             }
    21                         }
    22                         else if (!match[1]) {  //选择 tag
    23                             if (parentNode.nodeName == match[2].toUpperCase()) {
    24                                 isMatch[i] = true;
    25                                 matchParentNode = parentNode;
    26                             }
    27                         }
    28                         parentNode = (parentNode == matchParentNode) ? matchParentNode.parentNode : parentNode.parentNode;
    29                     }
    30                     parentNode = matchParentNode;
    31                 }
    32                 //isMatch为判定标志位,其长度应该等于父选择条件的个数,且每一项都为true才能判定为选中
    33                 result = (isMatch.length == aQuery.length - 1) && !(/,,/.test(("," + isMatch.join(',') + ","))) ? elem : null;
    34                 return result;
    35             }
    36   }

    说明:

    为了方便理解,说明部分至于代码注释处。

    读到这里,如果你还是没太了解代码的意图,这里再给你说下流程,方便你看代码

    五部分代码:简称为a、b、c、d、e

    a和b是一块,其为静态方法,也就是辅助用的函数,a目的是遍历作用,b为兼容得到按类查找节点

    c、d、e为一块,其为原型方法,仿照Sizzle的思路,例如:$(".class1 .class2 .class3")

    第一步:利用select方法查找所有的样式类名为class3的元素集合

    第二部:然后将集合丢给filter方法,进行".class1 .class2“的判定查找其父节点符合的类名是否为class2,若有则设在isMacth[1]=true,负责继续查找上层父节点,直到body节点,最后将结果集join(","),如果全满足条件的话会得到”,true,true,“这种字符串,所以利用”,,“这种连续逗号的方式判定,有的标志位没有true

    第三部:返回init中得到满足条件的元素集合,创造类数组并返回。

    自此,成功构造出支持id、类名、标签的路径CSS查找器

    (本篇至此,其他内容未完,待续……)

    下一节提示:

    趁热打铁,打造find原型方法,并完整扩展init方法

  • 相关阅读:
    iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(二)
    iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一)
    iOS开发UI篇—简单介绍静态单元格的使用
    iOS开发UI篇—UITableview控件使用小结
    ios开发UI篇—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
    iOS开发UI篇—使用xib自定义UItableviewcell实现一个简单的团购应用界面布局
    iOS开发UI篇—使用嵌套模型完成的一个简单汽车图标展示程序
    iOS开发UI篇—实现UItableview控件数据刷新
    iOS开发UI篇—推荐两个好用的Xcode插件(提供下载链接)
    人人都应学习的公链知识——比原总体架构
  • 原文地址:https://www.cnblogs.com/ccto/p/3086923.html
Copyright © 2020-2023  润新知