【99JS手记】之一:nth-child选择器

因为本人css很烂很烂,而且水平超烂,所以不敢写特效来误导人,所以,99js手记,是一个纯javascript的频道,利用javascript的各种特性,实现精彩的效果~这一系列的js手记呢,定位的是初中级的js开发人员,目的是多多的写代码~而不是把时间都放在排页面上~在文章中,我会引用大量的资料,希望大家可以认真的阅读。我引用的大多是火狐开发者社区等这样的权威资料,希望大家也养成在上面查阅资料的习惯~

今天开篇,也非常高兴大家能捧场,今天实现一个很有意思的东东

目标: nth-child选择器。

nth-child选择器(不了解可以点这里)对css3与jquery了解的童鞋都应该知道这个选择器。他的语法是

element:nth-child(an + b) { style properties }

这里我们利用js模拟,目标是拿到一个元素集合,利用nthchild(元素集合,an + b)方法来获得想要的结果

一些例子:

  1. nthchild(lista,2n+1),
  2. nthchild(lista,2),
  3. nthchild(lista,3n),
  4. nthchild(lista,n+2),
  5. nthchild(lista,-n+2)

思路:

1.选择器,:顾名思义,首先要有一个范围来选择,那么我们的范围自然就是前面的节点,比如span,或者是已经获取完毕的一个list.(补充知识:domlist,跟arguments(函数参数)一样,是一个类数组,这是啥意思呢?http://shifteleven.com/articles/2007/06/28/array-like-objects-in-javascript

  1. 它具有下标[0][1][2],它可以取到length
  2. 它没有数组的方法,这一点很重要

关于更多的知识可以看:

  1. http://www.cr173.com/html/12597_1.html
  2. http://tuoxie.me/blog/how-to-make-array-like-object-in-javascript

值得补充的是 jquery对象也是个类数组,虽然他有一些数组方法,但都是人为加上去的。扯远了。。

2.我们需要根据“输入”来确定参数。因为我们知道 nth-child(2n+1),nth-child(2), nth-child(3n), 等等的处理办法是完全不同的。这个技术最好的工具是:"正则表达式”由于正则表达式无比艰深,这里提供经典教程 30分钟掌握正则表达式- - http://manual.phpv.net/regular_expression.html利用正则表达式,我们希望获得的是 是否有n? 什么符号?数字是多少

3.分支处理,当我们获得nth-child内的参数后,我们就可以根据情况来处理了。分为以下几种:

  1. 纯数字 2
  2. 倍数 2n
  3. 混合型 n+2,2n+1之类

4 处理思路:x*n+y,n从0开始++,当然(x*n+y)要>0那我们可以设结果是 index 则 x*n+y=indexn = (index-y) / x ; 也就是说 x可以整除 index-y那么,当result满足:(index-y) % x === 0 且n >= 0,就是符合条件的了.之后我们遍历所有节点,判断下数量即可~另外注意一点,nth-child里面是从1开始的,所以遍历的时候一定要记得序号从1开始!

5 处理结果: 选择器通常返回的是一个数组,那么我们应该也要返回一个数组。可以利用空数组的push()方法(参考http://www.w3cschool.cn/jsref_push.html)

实现

1.实现正则匹配参数:正则的写法技巧是“找通性,考虑特殊”。

我们分析一下共性跟特殊,2n+1,2,3n,-n+2,n-1共性:格式都是 数字*n +-数字 的形式;特殊 :可能有负号,可能没有n.根据这个思路我写一个正则表达式。也欢迎大家写出更好的来~

(-?\d*)[n]*([+-]\d+)*
  1. *表示前面的匹配 0-无数个
  2. [n]就表示一个n啦
  3. [+-]表示 一个“范围”就是说既有可能匹配加号,也有可能匹配减号
  4. \d表示匹配一个数字
  5. -? 表示 减号匹配0个或者一个,也就是说有可能有
  6. \d+ 加号表示 匹配一个或者多个

加一个括号表示“捕获组” 这是啥意思呢,其实就是让电脑在内存里开几个房子来存储这一小部分~我们之后可以利用match方法等等,再次访问被捕获组匹配到的部分.因为正则表达式是很多javascript函数的核心,所以这里讲得比较细,也希望大家回去多多学习正则。

2 利用正则抓出变量:我们可以利用javascript的match方法,来观察下正则的效果。(match方法的讲解http://www.jb51.net/w3school/js/jsref_match.htm)

var reg = /(-?\d*)[n]*([+-]\d+)*/
var a = '2'
a.match(reg)
//["2", "2", undefined]
var b = '2n+1'
b.match(reg)
//["2n+1", "2", "+1"]
var c= '-n+1'
c.match(reg)
//["-n+1", "-", "+1"]
var d = '10n+10'
d.match(reg)
//["10n+10", "10", "+10"]

非常明显,match的返回结果 在不同情况下表现是不同的。返回结果是一个数组,数组的第一项是“全局匹配”,即正则全部匹配到的内容,第二项跟第三项是我们所说的“捕获组” 可以叫做 捕获组1号,捕获组2号,再回头看

var reg = /(-?\d*)[n]*([+-]\d+)*/

是不是就明白多了。。

3开始处理:我们的核心函数利用拿到的值做分支判断

var reg = /(-?\d*)[n]*([+-]\d+)*/,
m = selector.match(reg);
if (selector === m[1]) { // 纯数字
  return [nodeList[(parseInt(m[1]) - 1)]];
}
function filter(index) { //过滤器函数
  console.log('m1'+m[1],'m2'+m[2])
  x = m[1];//直接把n前面的值赋给x
  y= m[2];
  if (m[2]) { // n后面存在y
  if ('' === m[1]) { // n前面木有负号 xn+y x=1
    x= 1;
    y= m[2]
  } else if ('-' === m[1]) { // n前面有负号
    x = -1;
    y= m[2]
  }
  } else { //n后面不存在y
    y = 0;
  }
  var n = (index - parseInt(y)) / parseInt(x);//开始计算
  console.log('x'+x,'y'+y,n+'n')
  // 正整数返回true
  return (n === parseInt(n) && n >= 0) ? true : false;
}

以上用了一个函数 parseInt 这是干嘛的呢,请参见http://www.w3school.com.cn/js/jsref_parseInt.asp;之后呢,我们开始遍历我们的dom节点。

var result = []; //存储结果
var l = nodeList.length;//缓存
for (var i =1; i<=l; i++) {//这里从1开始!
  console.log(i,filter(i))
  if (filter(i)) {
    result.push(nodeList[i-1])//注意了nodelist下标从0开始,所以要-1!
  }
}

补充(:这里我再次需要讲解一个东西叫做高性能循环.一般我们写循环通常是这样的

for(var i =0;i<nodelist.length;i++)

这样写有啥坏处呢?我们仔细分析for的结构.for(初始值;布尔值,表示满足条件后结束循环;操作)这样电脑每次都要计算一下i<nodelist.length这个布尔值不是么。 因此我们可以直接这么写

var l = nodeList.length;
for (var i = nodeList.length; i--; ) 

他的效力跟

for(var i =0;i<nodelist.length;i++) 

是一样的,但是性能更高。写了这么多,我们可以把完整的函数放出来了

	
function nthChild(nodeList, selector) {
  var x,
  y; //xn+y
  var reg = /(-?\d*)[n]*([+-]\d+)*/,
  m = selector.match(reg);
  if (selector === m[1]) { // 纯数字
   return [nodeList[(parseInt(m[1]) - 1)]];
  }
function filter(index) { //过滤器函数
  console.log('m1' + m[1], 'm2' + m[2])
  x = m[1]; //直接把n前面的值赋给x
  y = m[2];
  if (m[2]) { // n后面存在y
  if ('' === m[1]) { // n前面木有负号  xn+y x=1
    x = 1;
    y = m[2]
  } else if ('-' === m[1]) { // n前面有负号
    x = -1;
    y = m[2]
  } else { //n后面不存在y
    y = 0;
  }
  var n = (index - parseInt(y)) / parseInt(x); //开始计算
  console.log('x' + x, 'y' + y, n + 'n')
  // 正整数返回true
  return (n === parseInt(n) && n >= 0) ? true : false;
 }
 var result = []; //存储结果
 var l = nodeList.length; //缓存
 for (var i = 1; i <= l; i++) { //这里从1开始!
   console.log(i, filter(i))
   if (filter(i)) {
     result.push(nodeList[i - 1]) //注意了nodelist下标从0开始,所以要-1!
   }
 }
 return result;
}

大家注意到很多console.log了么,这是常用的javascript调试技巧,我这里只用了他很小的一个部分:显示当前的变量是多少。具体的用法可以参考: http://www.ruanyifeng.com/blog/2011/03/firebug_console_tutorial.html.另外ie是不支持console.log的,记得去掉不然会报错.大家可以测试一下。

var lis = document.getElementsByTagName('li')//取得所有的li标签元素,返回一个nodelist
nthChild(lis,'2n+1')
nthChild(lis,'2')
nthChild(lis,'n+5')

有关于:nth-child的介绍就先介绍到这,希望这样的杂记对您的工作与学习有所帮助,如果你感兴趣请继续观注w3cplus99js手记的相关更新。

如需转载,烦请注昨出处:http://www.w3cplus.com/js/js/99js-nth-child.html

返回顶部