jQuery入门笔记之(二)文档对象模型

基础 DOM 和 和 CSS

一. 设置元素及内容

我们通过前面所学习的各种选择器、过滤器来得到我们想要操作的元素。这个时候,我们就可以对这些元素进行 DOM 的操作。
那么,最常用的操作就是对元素内容的获取和修改。html()text()方法:

方法名 描述 区分
html() 获取元素中 HTML 内容 连同标签一起提取
html(value) 设置元素中 HTML 内容 清空原数据,设置html内容
text() 获取元素中文本内容 自动清理html标签
text(value) 设置元素中文本内容 自动转义html标签以文本形式呈现
val() 获取表单中的文本内容 value属性的值
val(value) 设置表单中的文本内容 设置value的值(有特殊用法)

注意:当我们使用 html()text()设置元素里的内容时,会清空原来的数据。而我们期望能够追加数据的话,需要先获取原本的数据。

1
2
$('#box').html($('#box').html() + '<em>guowenfh.github.io</em>'); //追加数据
//当然它还支持使用函数来进行更复杂的追加

val()的特殊用法:
如果想设置多个选项的选定状态,比如下拉列表、单选复选框等等,可以通过数组传递操作。

1
$("input").val(["check1","check2", "radio1"]); //value 值是这些的将被选定

二. 元素属性操作

除了对元素内容进行设置和获取,通过jQuery也可以对元素本身的属性进行操作,包括获取属性的属性值、设置属性的属性值,并且可以删除掉属性。
attr()removeAttr()

方法名 描述
attr(key) 获取某个元素 key 属性的属性值
attr(key, value) 设置某个元素 key 属性的属性值
attr({key1:value2, key2:value2...}) 设置某个元素多个 key 属性的属性值
attr(key, function (index, value){}) 通过 fn 来设置设置某个元素 key属性

注意:

  1. jQuery中很多方法都可以使用 function() {}来返回出字符串,比如 html()text()val()和上一章刚学过的 is()filter()方法。
    这些方法里的function() {},可以不传参数。可以只传一个参数 index,表示当前元素的索引(从0开始)。也可以传递两个参数 indexvalue,第二个参数表示属性原本的值。
    (当然并不是所有方法都适合,有兴趣可以自己逐个尝试)。
  2. 删除指定的属性,这个方法就不可以使用匿名函数,传递indexvalue均无效。$('div').removeAttr('title'); //删除指定的属性
  3. 当然因为idclass也是元素的属性,自然也可以使用attr进行设置,但是建议不这样使用,因为会导致整个页面结构的混乱,有专门的属性对它们进行设置。

三. 元素样式操作

元素样式操作包括了直接设置 CSS 样式、增加 CSS 类别、类别切换、删除类别这几种操作方法。使用频率极高!

方法名 描述
css(name) 获取某个元素行内的 CSS 样式
css([name1, name2, name3]) 获取某个元素行内多个 CSS 样式
css(name, value) 设置某个元素行内的 CSS 样式
css(name, function (index, value) ) 设置某个元素行内的 CSS 样式
css({name1 : value1, name2 : value2}) 设置某个元素行内多个 CSS 样式,键值对
addClass(class) 给某个元素添加一个 CSS 类
addClass(class1 class2 class3...) 给某个元素添加多个 CSS 类
removeClass(class) 删除某个元素的一个 CSS 类
removeClass(class1 class2 class3...) 删除某个元素的多个 CSS 类
toggleClass(class) 来回切换默认样式和指定样式
toggleClass(class1 class2 class3...) 同上
toggleClass(class, switch) 来回切换样式的时候设置切换频率
toggleClass(function () {}) 通过匿名函数设置切换的规则
toggleClass(function () {}, switch) 在匿名函数设置时也可以设置频率
toggleClass(function (i, c, s) {}, switch) 在匿名函数设置时传递三个参数

理解:

在获取多个 CSS 样式时,获取到的是一个对象数组,如果用原生JS进行解析需要使用for in遍历。

1
2
3
4
var box = $('div').css(['color', 'height', 'width']); //得到多个 CSS 样式的数组对象
for (var i in box) { //逐个遍历出来
alert(i + ':' + box[i]);
}

在jQuery显然不需要那么麻烦,因为它提供了一个遍历工具专门来处理这种对象数组,$.each()方法,这个方法可以轻松的遍历对象数组。

1
2
3
4
5
6
7
8
9
/**
* 遍历对象数组
* @param {String} index 索引,鍵,属性名
* @param {String} value 属性值,值(相当于arr[i])
*/
//遍历原生JS对象数组时,value为元素DOM。
$.each(box, function(index, value) {
alert(index+":"+value);//效果和上面的一样
});

如果想设置某个元素的 CSS 样式的值,但这个值需要计算我们可以传递一个匿名函数。

1
2
3
$('div').css('width', function (index, value) {
return (parseInt(value) - 50) + 'px';//局部操作,不影响全局,避免冲突。
});

在使用.toggleClass()使用时,可以使用传入匿名函数的方法,实现由默认到几个class之间的切换。例如:

1
2
3
4
5
6
7
8
9
10
11
12
//注意这里必须要先删除原有的样式,不然只是被覆盖了,而不是替换。
$('div').click(function() {
$(this).toggleClass(function() {
if ($(this).hasClass('red')) {
$(this).removeClass('red');
return 'blue';
} else {
$(this).removeClass('blue');
return 'red';
}
});
});

对于.toggleClass()传入匿名函数的方法,还可以可以传递 index索引、class类两个参数以及频率布尔值,可以得到当前的索引、class类名和频率布尔值。

四. CSS方法

(一)width()方法

方法名 描述
width() 获取某个元素的宽度(number)
width(value) 设置某个元素的宽度(无单位时,默认px)
width(function (index, width) {}) 通过匿名函数设置某个元素的宽度

虽然可以不加单位,但是建议加上,使代码更加清晰。
index 是索引,value 是原本值。

(二)height()方法

方法名 描述
height() 获取某个元素的高度
height(value) 设置某个元素的高度
height(function (index, height) {}) 通过匿名函数设置某个元素的高度

上述两个方法不包括内外边距和边框

(三)内外边距和边框尺寸方法

方法名 描述
innerWidth() 获取元素宽度,包含内边距 padding
innerHeight() 获取元素高度,包含内边距 padding
outerWidth() 获取元素宽度,包含边框 border 和内边距 padding
outerHeight() 获取元素高度,包含边框 border 和内边距 padding
outerWidth(ture) 同上,且包含外边距(注意里面的true)
outerHeight(true) 同上,且包含外边距

(四)元素偏移方法

方法名 描述
offset() 获取某个元素相对于视口的偏移位置(无论定位与否)
position() 获取某个元素相对于父元素的偏移位置
scrollTop() 获取垂直滚动条的位置
scrollTop(value) 设置垂直滚动条的位置
scrollLeft() 获取水平滚动条的值
scrollLeft(value) 设置水平滚动条的值

注意:
前两个方法,获取得到的是一个对象,如:{left:12,top:32},所以需要获取其中的一个值的时候,还需要进行选取,例如:$(div).offset.left。获取相对与视口的偏移。

在获取滚动条的值时需要注意的是,对象为window且需要用$包装转化成jQ对象

1
2
$(window).scrollTop(); //获取当前滚动条的位置
$(window).scrollTop(300); //设置当前滚动条的位置

DOM 节点操作

DOM中有一个非常重要的功能,就是节点模型,也就是DOM中的“M”。页面中的元素结构就是通过这种节点模型来互相对应着的,我们只需要通过这些节点关系,可以创建、插入、替换、克隆、删除等等一些列的元素操作。

一. 创建节点

为了使页面更加智能化,有时我们想动态的在 html 结构页面添加一个元素标签,那么在插入之前首先要做的动作就是:创建节点。
在jQuery中创建节点异常简单。

1
2
var box = $('<div id="box">节点</div>'); //创建一个节点
$('body').append(box); //将节点插入到<body>元素内部

二. 插入节点

在创建节点的过程中,其实我们已经演示怎么通过.append()方法来插入一个节点。但仅仅这个功能远远不能满足我们的需求,除了这个方法,jQuery提供了其他几个方法来插入节点。

内部插入节点方法

方法名 描述
append(content) 向指定元素内部后面插入节点content
append(function (index, html) {}) 使用匿名函数向指定元素内部后面插入节点
appendTo(content) 将指定元素移入到指定元素content 内部后面
prepend(content) 向指定元素content 内部的前面插入节点
prepend(function (index, html) {}) 使用匿名函数向指定元素内部的前面插入节点
prependTo(content) 将指定元素移入到指定元素 content 内部前面

需要注意的是appendTo(content)prependTo(content)在使用方法上与其他两个略微有些不同,$("<em>new</em>").appendTo("div");,它代表的是创建的节点传入div内部。

匿名函数方式:

1
2
3
4
5
6
$('div').append(function (index, html) {
//使用匿名函数插入节点index是获取到的div的索引,html 是原节点
if(index==1){
return '<strong>节点</strong>';//在获取到的第二个div内部添加节点
}
});

外部插入节点方法

方法名 描述
after(content) 向指定元素的外部后面插入节点 content
after(function (index, html) {}) 使用匿名函数向指定元素的外部后面插入节点
before(content) 向指定元素的外部前面插入节点 content
before(function (index, html) {}) 使用匿名函数向指定元素的外部前面插入节点
insertAfter(content) 将指定节点移到指定元素 content 外部的后面
insertBefore(content) 将指定节点移到指定元素 content 外部的前面

与上面相同insertAfter(content)insertBefore(content)也与其它两个是相反的。

三.包裹节点

jQuery 提供了一系列方法用于包裹节点,那包裹节点是什么意思呢?其实就是使用字符串代码将指定元素的代码包含着的意思。

方法名 描述
wrap(html) 向指定元素包裹一层 html 代码
wrap(element) 向指定元素包裹一层 DOM 对象节点
wrap(function (index) {}) 使用匿名函数向指定元素包裹一层自定义内容
unwrap() 移除一层指定元素包裹的内容(多层需移除多次)
wrapAll(html) 用 html 将所有元素包裹到一起
wrapAll(element) 用 DOM 对象将所有元素包裹在一起
wrapInner(html) 向指定元素的子内容包裹一层 html
wrapInner(element) 向指定元素的子内容包裹一层 DOM 对象节点
wrapInner(function (index) {}) 用匿名函数向指定元素的子内容包裹一层

wrap的多种用法:

1
2
3
4
5
6
7
8
$('div').wrap('<strong class="ing"></strong>'); //在 div 外层包裹一层 strong
$('div').wrap('<strong>123</strong>'); //包裹的元素可以带内容
$('div').wrap('<strong><em></em></strong>'); //包裹多个元素
$('div').wrap($('strong').get(0)); //也可以包裹一个原生 DOM
$('div').wrap(document.createElement('strong')); //临时的原生 DOM
$('div').wrap(function (index) { //匿名函数
return '<strong></strong>';
});

注意: .wrap().wrapAll()的区别在前者把每个元素当成一个独立体,分别包含一层外层;后者将所有元素作为一个整体作为一个独立体,只包含一层外层。这两种都是在外层包含,而.wrapInner()在内层包含。

四. 节点操作

方法名 描述 参数解析
$('div').clone(true); 复制一个节点 true时表示同时复制事件行为,空或false都表示只复制元素及内容
$('div').remove("#box"); 删除一个id=box的div元素 无参数时,表示直接删除元素
$('div').detach(); 保留事件行为的删除 同上
$('div').empty(); 清空节点里的内容 无参数
$(selector).replaceWith(content); 将 $(selector) 替换成 content 元素 HTML字符串,DOM元素,或者jQuery对象
$(content).replaceAll(selector); 同上 参数selector为被替换的元素,content为替换的内容。

注意:

  1. .remove().detach()都是删除节点,而删除后本身方法可以返回当前被删除的节点对象,但区别在于前者在恢复时不保留事件行为,后者则保留。
  2. 节点被替换后,所包含的事件行为就全部消失了。
  3. 注意replaceWith()replaceAll()方法替换的操作的内容和被替换元素所在的位置不同

jQuery入门笔记之(一)选择器引擎

本来是单独整理了一个CSS选择器的,但是在jQuery中基本都有对应的,所以就不发了。

jQuery选择器,若未作特别说明,获取的都是元素集合。

一. 常规选择器

(一)简单选择器

模仿的是CSS选择器,只不过在使用jQuery选择器时,我们首先必须使用“$()”函数来包装我们的 CSS 规则。
而CSS 规则作为参数传递到jQuery对象内部后,再返回包含页面中对应元素的 jQuery 对象。随后可以进行节点操作,例如:$('#box').css('color', 'red');

那么除了 ID 选择器之外,还有两种基本的选择器,分别为:元素标签名和类(class):

选择器 CSS 模式 jQuery 模式 描述
元素名 div{} $('div') 获取所有div元素的 DOM 对象
ID #box {} $('#box') 获取一个 ID 为 box 元素的 DOM 对象
类(class) .box{} $('.box') 获取所有class为box的所有DOM对象

我们可以采用jQuery核心自带的一个属性length来查看返回的元素个数。(size()方法已经弃用)

在实践过程中发现使用多个id时,css居然都会高亮,jQuery没有这个问题。(标准写明一个页面只能有一个id)

jQuery选择器的写法与CSS选择器十分类似,只不过他们的功能不同。CSS 找到元素后添加的是单一的样式,而jQuery则添加的是动作行为。重要的是jQuery兼容性更好,例如:

1
2
3
4
#box > p { //CSS 子选择器,IE6 不支持
color:red;
}
$('#box > p').css('color','red'); //jQuery 子选择器,兼容了 IE6

jQuery选择器支持CSS1、CSS2的全部规则,支持CSS3部分实用的规则,同时它还有少量独有的规则而jQuery选择器在获取节点对象的时候不但简单,还内置了容错功能区别如下:

1
2
$('#pox').css('color', 'red'); //不存在ID为pox的元素,也不报错
document.getElementById('pox').style.color = 'red'; //报错了

如何判断jQuery是否调取不存在的元素:

1
2
3
4
5
6
if ($('#pox').length > 0) { //jQuery对象,判断元素包含数量即可
$('#pox').css('color', 'red');
}
//或者转化成DOM对象方式判断
if ($('#pox')[0]) {}; //通过数组下标也可以获取 DOM 对象
if ($('#pox').get(0)) {} ;

(二)进阶选择器

在简单选择器中,我们了解了最基本的三种选择器:元素标签名、ID 和类(class)。那么在基础选择器外,还有一些进阶和高级的选择器方便我们更精准的选择元素

选择器 CSS 模式 jQuery 模式 描述
群组选择器 span,.con,.box{} $('span,em,.box') 获取多个选择器的 DOM 对象
后代选择器 ul li a{} $('ul li a') 获取追溯到的多个 DOM 对象
通配选择器 *{} $('*') 获取所有元素标签的 DOM 对象

目前介绍的六种选择器,在实际应用中,我们可以灵活的搭配,使得选择器更加的精准和快速:

1
$('#box p, ul li *').css('color', 'red');//组合了多种选择器

警告:在实际使用上,通配选择器一般用的并不多,一般只用在局部的环境内。因为在大通配上,比如:$('*'),这种使用方法效率很低,影响性能,建议尽可能的少用。(CSS上也很少用)

还有一种选择器,可以在ID和类(class)中指明元素前缀,比如:

1
2
$('div.box'); //限定.box获取到的元素标签名必须是div
$('p#box div.side'); //同上

如同CSS一样,类(class)有一个特殊的模式,就是同一个DOM节点可以声明多个类(class)。那么对于这种格式,我们有多class选择器可以使用,但要注意和class群组选择器的区别

1
2
3
4
.box.pox { //双 class 选择器获取页面中class同时有.box.pox的元素
color:red;
}
$('.box.pox').css('color', 'red'); //用多个class进行精准确定

注意要点:

只追求必要的确定性。当选择器筛选越复杂,jQuery内部的选择器引擎处理字符串的时间就越长。

(三)高级选择器

在前面学习了六种最常规的选择器,一般来说通过这六种选择器基本上可以解决所有DOM节点对象选择的问题。但在很多特殊的元素上,比如父子关系的元素,兄弟关系的元素,特殊属性的元素等等并不好获取,下面就来说说这些高级选择器:

选择器 CSS 模式 jQuery 模式 描述
后代选择器 ul li a {} $('ul li a') 获取追溯到的多个 DOM 对象
子选择器 div > p {} $('div p') 只获取子类节点的多个 DOM 对象
next 选择器(相连) div + p {} $('div + p') 只获取某节点后一个同级DOM对象
nextAll 选择器(之后所有) div ~ p {} $('div ~ p') 获取某节点后面所有同级DOM对象

其实后代选择器我们在进阶选择器里面已经有过使用,这里为什么要再提起呢?

因为它属于层次选择器,在高级选择器中,jQuery为这样的选择器都提供了一个相对应的方法。

  1. jQuery为后代选择器提供了一个等价的find()方法:
    1
    2
    $('#box p').css('color', 'red'); //后代选择器
    $('#box').find('p').css('color','red');//和后代选择器等价
  2. jQuery为子选择器提供了一个等价的children()方法
    1
    2
    $('#box > p').css('color','red');//子选择器,孙子失明
    $('#box').children('p').css('color','red');//和子选择器等价
  3. jQuery为next选择器提供了一个等价的next()方法:
    1
    2
    $('#box+p').css('color','red');//下一个同级节点
    $('#box').next('p').css('color','red');//next选择器等价
  4. jQuery为nextAll选择器提供了一个等价的方法nextAll():
    1
    2
    $('#box ~ p').css('color','red');//后面所有同级节点
    $('#box').nextAll('p').css('color', 'red'); //和 nextAll 选择器等价

需要注意的是:

  1. 层次选择器对节点的层次都是有要求的,比如子选择器,有子节点才可以被选择到,孙子节点和重孙子节点都无法选择到。next和nextAll选择器,必须是同一个层次的后一个和后N个,不在同一个层次就无法选取到了。
  2. find()children()next()nextAll()和这四个方法中,如果不传递参数,就相当于传递了“”,选择所有符合条件的元素,*建议尽量保持参数的传递

jQuery独有补充方法。CSS未含有

1
2
3
4
5
6
7
$('#box').prev('p').css('color','red');//同级上一个元素
$('#box').prevAll('p').css('color','red');//同级上面所有的元素

$('#box').prevUntil('p').css('color','red');//同级上非指定元素选定,遇到则停止
$('#box').nextUntil('p').css('color','red');//同级下非指定元素选定,遇到则停止

$('#box').siblings('p').css('color','red');//siblings()方法正好集成了prevAll()和nextAll()两个功能的效果,及上下相邻的所有元素进行选定:

扩展:

1
2
3
$('p', '#box');//jQuery会自动把这条语句转成$('#box').find('p'),这会导致一定的性能损失。

$('p', $('#parent'));//jQuery内部会也将这条语句转成$('#box').find('p')

这里,推荐使用jQuery提供的方法。使用“+”或“~”从字面上没有next和nextAll更加语义化,更加清晰,jQuery的方法更加丰富,提供了相对的prev和prevAll。
并且有时需要能够灵活的拆分和组合选择器。所以,如果jQuery提供了独立的方法来代替某些选择器的功能,推荐优先使用独立的方法。

(四)属性选择器

注意¦应该是|,因为markdown表格解析的问题,所以用来替代

CSS 模式 jQuery模式 描述
a[title] $('a[title]') 获取具有这个属性的 DOM 对象
a[title=num1] $('a[title=num1]') 获取具有这个属性=这个属性值的DOM对象
a[title^=num] $('a[title^=num]') 获取具有这个属性且开头属性值匹配的DOM对象
a[title¦=num] $('a[title¦=num]') 获取具有这个属性且等于属性值或开头属性值匹配后面跟一个-号的DOM对象
a[title$=num] $('a[title$=num]') 获取具有这个属性且结尾属性值匹配的DOM对象
a[title!=num] $('a[title!=num]') 获取不具有这个属性或不等于该属性值的DOM对象
a[title~=num] $('a[title~=num]') 获取具有这个属性且属性值是以一个空格分割的列表,其中包含属性值的DOM对象
a[title*=num] $('a[title*=num]') 获取具有这个属性且属性值含有一个指定字串的DOM对象
a[bbb][title=num1] $('a[bbb][title=num1]') 获取具有这个属性且属性值匹配的DOM对象

二. 过滤选择器

(一)基本过滤器

过滤器名 jQuery 语法 说明 返回
:first $('li:first') 选取第一个元素 单个
:last $('li:last') 选取最后一个元素 单个
:not(selector) $('li:not(.red)') 选取class不是red的li元素 集合
:even $('li:even') 选择索引(以下几个都是从0开始)是偶数的所有元素 集合
:odd $('li:odd') 选择索引是奇数的所有元素 集合
:eq(index) $('li:eq(2)') 选择索引等于index的元素(负数从后开始) 单个
:gt(index) $('li:gt(2)') 选择索引大于index的元素 集合
:lt(index) $('li.lt(2)') 选择索引小于index的元素 集合
:header $(':header') 选择标题元素,h1~h6 集合
:animated $(':animated') 选择正在执行动画的元素 集合
:focus $(':focus') 选择当前被焦点的元素 集合

注意::focus过滤器,必须是网页初始状态的已经被激活焦点的元素才能实现元素获取。而不是鼠标点击或者Tab键盘敲击激活的。

1
2
$('input').get(0).focus(); //先初始化激活一个元素焦点
$(':focus').css('background', 'red'); //被焦点的元素

jQuery为最常用的过滤器提供了专用的方法,如下:

1
2
3
4
$('li').eq(2).css('background','#ccc');//元素li的第三个元素,负数从后开始
$('li').first().css('background','#ccc');//元素li的第一个元素
$('li').last().css('background','#ccc');//元素li的最后一个元素
$('li').not('.red').css('background','#ccc');//元素li不含class为red的元素

(二)内容过滤器

内容过滤器的过滤规则主要是包含的子元素或文本内容上。

过滤器名 jQuery语法 说明
:contains(text) $(':contains("ycku.com")') 选取含”ycku.com”文本的元素
:empty $(':empty') 选取不包含子元素或空文本的元素
:has(selector) $(':has(.red)') 选取含有class是red的元素(在父元素调用)
:parent $(':parent') 选取含有子元素或文本的元素

jQuery 提供了一个 has()方法来提高:has 过滤器的性能:

1
$('ul').has('.red').css('background', '#ccc'); //选择子元素含有 class 是 red 的元素

jQuery提供了一个名称和:parent相似的方法,但这个方法并不是选取含有子元素或文本的元素,而是获取当前元素的父元素,返回的是元素集合。

1
2
3
$('li').parent().css('background','#ccc');//选择当前元素的父元素
$('li').parents().css('background','#ccc');//选择当前元素的父元素及祖先元素(追溯到html)
$('li').parentsUntil('html').css('background','#ccc');//选择当前元素遇到html父元素停止(会在body上加)

(三)可见性过滤器

可见性过滤器根据元素的可见性和不可见性来选择相应的元素。

过滤器名 jQuery 语法 说明
:hidden $(':hidden') 选取所有不可见元素
:visible $(':visible') 选取所有可见元素

注意::hidden过滤器一般是包含的内容为:CSS样式为display:none、input表单类型为type="hidden"visibility:hidden的元素。

(四)子元素过滤器

子元素过滤器的过滤规则是通过父元素和子元素的关系来获取相应的元素。

|过滤器名|jQuery语法|说明|
|—|—|—|—|
|:first-child|$('li:first-child')|获取每个父元素的第一个子元素|
|:last-child|$('li:last-child')|获取每个父元素的最后一个子元素|
|:only-child|$('li:only-child')|获取有且只有一个子元素的元素|
|:nth-child(odd/even/index)支持表达式,如2n等同even|$('li:nth-child(even)')|获取每个自定义子元素的元素(索引值从 1 开始计算)|

(五)其他方法

jQuery 在选择器和过滤器上,还提供了一些常用的方法,方便我们开发时灵活使用。

方法名 jQuery 语法 描述
is(s/o/e/f) $('li').is('.red')返回布尔值 传递选择器、DOM、jquery对象或是函数来匹配元素集合,如果这些元素中至少有一个元素匹配给定的参数,返回true
hasClass(class) $('li').eq(2).hasClass('red') 其实就是is("." + class),但只能传递class
slice(start, end) $('li').slice(0,2) 选择从start到end位置的元素,如果是负数,则从后开始
filter(s/o/e/f) $('li').filter('.red') 筛选元素集合中匹配表达式或通过传递函数测试的那些元素集合。
end() $('div').find('p').end() 获取当前元素的前一个状态的元素(同级或父级)
contents() $('div').contents() 获取某元素下面所有元素节点,包括文本节点,如果是iframe,则可以查找文本内容

is:

1
2
3
4
$('.red').is('li'); //选择器,检测class为是否为 red
$('.red').is($('li')); //jQuery 对象集合,同上
$('.red').is($('li').eq(2)); //jQuery 对象单个,同上
$('.red').is($('li').get(1)); //DOM 对象,同上

还可以进行各种自定义判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
<ul>
<li>list <strong>item 1</strong></li>
<li><span>list item 2</span></li>
<li>list item 3</li>
</ul>
<script>
$("ul").click(function(event) {
var $target = $(event.target);
if ( $target.is("li") ) {
$target.css("background-color", "red");
}
});
</script>

当用户点击的是第一个列表项中的单词"list"或第三个列表项中的任何单词时,被点击的列表项会被设置为红色背景。
不过,当用户点击第一个列表项中的item1或第二个列表项中的任何单词时,都不会有任何变化,这是为这上面的情况中,事件的目标分别是 <strong> <span>

slice:

1
$('li').slice(0,2).css('color', 'red'); //前三个变成红色

注意:这个参数有多种传法和JavaScript中的slice方法是一样的比如:slice(2),从第三个开始到最后选定;slice(2,4),第三和第四被选定;slice(0,-2),从倒数第三个位置,向前选定所有;slice(2,-2),前两个和末尾两个未选定。

filter:

1
2
3
4
5
6
$('li').filter('.red').css('background','#ccc');//选择li的class为red的元素
$('li').filter('.red,:first,:last').css('background','#ccc');//增加了首尾选择
//特殊要求函数返回
$('li').filter(function(){
return $(this).attr('class')=='red' && $(this).attr('title')=='列表3';
}).css('background', '#ccc');

此处注意$(this)的使用,这把this包装成了jQuery对象,以便使用jQuery的方法。

三. 表单选择器

(一)常规选择器

其实使用上面的选择器已经能对表单元素进行选取了,先来验证一下,来看看如何利用上面的方法来进行表单元素的选择。

1
2
3
4
5
//val()是jQuery用来获取表单元素文本内容的一个方法
$('input').val(); //元素名定位,默认获取第一个
$('input').eq(1).val(); //同上,获取第二个
$('input[type=password]').val();//选择type为password的字段
$('input[name=user]').val(); //选择 name 为 user 的字段

很显然,上面的这个方法都能选择到想要的元素,那么对于 id 和class基本一致,也可以结合属性选择器来精确的定位,在这里我们不在重复。
对于表单中的其他元素名比如:textarea、select 和 button 等,原理一样,不在重复。
但是这样是不是太过于复杂了呢?假如我们要同时选择input、textarea、select 和 button?继续看吧。

(二)表单选择器

虽然可以使用常规选择器来对表单的元素进行定位,但有时还是不能满足开发者灵活多变的需求,而且也太过于繁琐。所以,jQuery为表单提供了专用的选择器。

方法名 描述
:input 选取所有 inputtextareaselectbutton元素
:text 选择所有单行文本框,即 type=text
:password 选择所有密码框,即 type=password
:radio 选择所有单选框,即 type=radio
:checkbox 选择所有复选框,即 type=checkbox
:submit 选取所有提交按钮,即 type=submit
:reset 选取所有重置按钮,即 type=reset
:image 选取所有图像按钮,即 type=image
:button 选择所有普通按钮,即 button 元素
:file 选择所有文件按钮,即 type=file
:hidden 选择所有不可见字段,即 type=hidden

注意:

  1. 由于这些选择器都是返回元素集合,如果想获取某一个指定的元素,最好结合一下属性选择器。比如:
    1
    $(':text[name=user]).size(); //获取单行文本框 name=user 的元素
  2. 在使用这些属性时最好界定父元素,比如直接$(":hidden").length这样是不正确的,因为它还会选择到head标签内的元素,所以length属性不会为0,建议使用这样的形式:$("form :hidden")

(三)表单过滤器

jQuery 提供了四种表单过滤器,分别在是否可以用、是否选定来进行表单字段的筛选过滤。

方法名 描述
:enabled 选取所有可用元素
:disabled 选取所有不可用元素
:checked 选取所有被选中的元素,单选和复选字段
:selected 选取所有被选中的元素,下拉列表

jQuery入门笔记之(零)思考与基础核心

思考篇

一. 什么是 jQuery?

jQuery是一个JavaScript库,它通过封装原生的JavaScript函数得到一整套定义好的方
法。
它的作者是JohnResig,于2006年创建的一个开源项目,随着越来越多开发者的加入,jQuery已经集成了JavaScript、CSS、DOM和Ajax于一体的强大功能。
它可以用最少的代码,完成更多复杂而困难的功能,从而得到了开发者的青睐。

jQuery的版本

随着jQuery的不断进化,发展也变成了两条进化线:

2006 年 8 月发布了 jQuery1.0,第一个稳定版本,具有对 CSS 选择符、事件处理和
Ajax 交互的支持。

2010 年 2 月发布了 jQuery1.4.2,添加了.delegate()和.undelegate()两个新方法,提升了灵活性和浏览器一致性,对事件系统进行了升级。

2011 年 1 月发布了 jQuery1.5,重写了 AJAX 组件,增强了扩展性和性能。

2013 年 5 月发布了 jQuery1.10,增加了一些功能。

到此,jQuery的以上版本都有非常好的浏览器兼容性,支持所有浏览器,当然包括了IE6/7/8。但是直到下面这条线的出现:

2013 年 4 月发布了 jQuery2.0,5 月发布了 jQuery2.0.2,一个重大更新版本,不在支持 IE6/7/8,体积更小,速度更快。

目前最新版本分别是jQuery 2.1.4 和jQuery 1.11.3 。

现在两条线同时发展,可供大家选择。

根据项目要求来选择版本,下面有介绍

关于版本学习的问题:

版本的版本号升级主要有三种:

  • 第一种是大版本升级,比如1.x.x升级到2.x.x,这种升级规模是最大的,改动的地方是最多的,周期也是最长的,jQuery从1.x.x到 2.x.x用了7年。
  • 第二种是小版本更新,比如1.7升级到1.8,改动适中,增加或减少了一些功能一般周期半年到一年左右。
  • 第三种是微版本更新,比如1.8.1升级到1.8.2,修复一些bug或错误之类。

版本的内容升级主要也有三种:

  • 第一种是核心库的升级,比如优化选择符、优化 DOM或者AJAX等;这种升级不影响开发者的使用。
  • 第二种是功能性的升级,比如剔除一些过时的方法、新增或增强一些方法等等;这种升级需要了解和学习。
  • 第三种就是 BUG 修复之类的升级,对开发者使用没有影响。

所以综上所述:
有一半左右的升级都是内部优化,升级到新版本并不需要任何学习成本。就算在新的版本增加了一些功能,只需要几分钟了解一下即可使用,无需清零之前的知识,只需后续累加。
当然,在早期的 jQuery 版本都创建了最常用的功能,而新版本中增加的功能,也不是最常用的,无需立即学习,立马用起。

选择一个版本开始学习吧!

二. jQuery 的功能和优势

jQuery 作为 JavaScript 封装的库,他的目的就是为了简化开发者使用 JavaScript。主要
功能有以下几点:

  1. 像 CSS 那样访问和操作 DOM
  2. 修改 CSS 控制页面外观
  3. 简化 JavaScript 代码操作
  4. 事件处理更加容易
  5. 各种动画效果使用方便
  6. 让 Ajax 技术更加完美
  7. 基于 jQuery 大量插件
  8. 自行扩展功能插件

jQuery 最大的优势,就是特别的方便。比如模仿 CSS 获取 DOM,比原生的 JavaScript
要方便太多。并且在多个 CSS 设置上的集中处理非常舒服,而最常用的 CSS 功能又封装到
单独的方法,感觉非常有心。
最重要的是 jQuery 的代码兼容性非常好,你不需要总是头疼着考虑不同浏览器的兼容问题。

其次像《编写高质量代码–web前端开发修炼之道》作者曹刘阳在微博上说的jq的强大真的只在那个$选择器吗?太小看jq了,私以为jq真正强大和坚挺的原因有3:
1、工业标准,我不知道未来还有谁能像jq一样,api上犀牛书。总之是前无古人,后面恐怕也难有来者。
工业标准有多可怕和难以撼动,会超出你想像。看看php就知道了。
2、jq的api设计对于原生js的改良。
3、jq丰富的插件积累。

我也疑惑过,随着现在前端框架angularreact的流行,乱花渐欲迷人。微博上神仙打架,疑惑的是我们这些前端初学者,不过我是这样认为的:
与其把时间花在前端框架的选择上。不如先把花在这个已经成为工业标准的库上,万物殊途同归 ,最终还是在JS做文章不是吗?

三. 是否兼容IE低版本

主要是从下面几个方面考虑:

1. 成本控制

  • 项目如果不是老站升级,也不是大门户的新闻站,成本控制和尽快
    上线测试才是最重要的。
  • 而如果新站一味要求全面兼容,会导致成本加剧(随着功能多少,
    成本倍率增加)。
  • 为了锁紧时间,就不停的加班再加班,又导致员工抵触,工作效率降低,这样成本不停的再累加。最终很多项
    目,根本没上线就失败了。

2. 用户选择

高质量用户和低质量用户

  • 例如一个3D游戏,和一款新闻应用:网易和腾讯在他们的新闻应用上,他们兼容了几乎所有的手
    机平台,比如 IOS、安卓、黑莓、塞班等等,因为新闻应用的核心在新闻,而新闻的用户基
    数巨大,需要兼顾高质量和低质量用户。而腾讯在 IOS 上的几十个应用,除了新闻、QQ、
    浏览器,其他的基本都只有 IOS 和安卓,在塞班和黑莓及其他上就没有了。
  • 所以,你的应用核心是哪方面?兼容的成本有多大?会不会导致成本控制问题?用户选
    择尤为重要,放弃低质量用户也是一种成本控制。
  • 在用户基数庞大的项目上,放弃低质量用户就有点愚笨,而你的用户基数只有 1000 人,而低质
    量用户有 50 人,那么为了这 50 人去做兼容,那么 3 倍的成本就变得非常的昂贵。

3. 项目侧重点

你的项目重点在哪里?是为了看新闻?是为了宣传线下产品?

那么你其实有必要兼容低版本浏览器。
首先这种类型的站不需要太好的用户体验,不需要太多的交互操作,只是看,
而兼容的成本比较低,并且核心在新闻或产品!

但如果你的项目有大量的交互、大量的操作,兼容成本较高,比如全球最大的社交网已经不兼容 IE6/7,就是这个原因。所以,项目并不是一味的全面兼容,或者全面不兼容,主要看你的项目侧重点在哪里!

4. 用户体验

如果你的项目在兼容低版本浏览器成本巨大,比如社交网,有大量的 JS 和 AJAX 操作。
那么兼容 IE6/7 的成本确实很高,如果兼容,用户体验就会很差。

兼容有两种:

  • 一种是高版本浏览器用性能好,体验好的模式;低版本的自动切换到兼容模式。
  • 第二种就是,不管高版本或低版本都用统一的兼容模式。

这两种成本都很高。用户体验好的模式,能增加用户粘度,增加付费潜在用户,而用户体验差的总是被用户归纳为心目中的备胎(所谓备胎就是实在没有了才去访问,如果有,很容易被抛弃)。

5. 数据支持

如果对某一种类型的网站项目有一定的研究,那么手头必须有支持的数据分析。
有数据分析可以更好的进行成本控制,更有魄力的解决高低质量用户的取舍。

6. 教育用户

很多项目可能是有固定客户群,或者使用该项目人员质量普遍较高。那么,面对零星一
点的低质量用户,我们不能再去迎合他。因为迎合他,就无法用高质量的用户体验去粘住忠实用户,又不能获取到低质量用户的芳心。
所以,我们应有的策略是:

  • 牢牢把握住高质量的忠诚用户,做到他们心目中的第一;
  • 教育那部分低质量用户(比如企业级开发项目,可以直接做企业培训,安装高版本浏览器等等。互联网项目,就给出提示安装高版本浏览器即可)。
  • 那么一部分低质量用户被拉拢过来,还有一小撮死性不改的就只有放弃。切不可捡了芝麻丢了西瓜,不要贪大求全。

结论就是:结论就是必须根据实际情况,你项目的成本情况、人员情况、用户情况和项目本身类型情况而制定,没有一刀切的
兼容或不兼容。

四. 下载及运行

目前最新版本分别是jQuery 2.1.4 和jQuery 1.11.3 。下载开发版,可以顺便读一读源代码。

下载jQuery:

参考手册:

第一个jQuery程序

1
2
3
4
5
6
7
8
9
//已经引入jQ,在body内写入如下代码
<button>按钮</button>
<script type="text/javascript">
$(function(){
$("button").click(function() {
alert("hello jQuery!");
});
});
</script>

基础核心

一. 代码风格

在jQuery程序中,不管是页面元素的选择、内置的功能函数,都是美元符号“$”来起
始的。而这个“$”就是jQuery当中最重要且独有的对象:jQuery对象,所以我们在页面元素选择或执行功能函数的时候可以这么写:

1
2
3
$(function () {}); //执行一个匿名函数
$(‘#box’); //进行执行的ID元素选择
$(‘#box’).css(‘color’, ‘red’); //执行功能函数

由于$本身就是jQuery对象的缩写形式,那么也就是说上面的三段代码也可以写成如下形式:

1
2
3
jQuery(‘#box’).css(‘color’, ‘red’);
//jQuery恒等于$
// console.log(jQuery===$);//true

且,每一次执行函数后,都会返回一个jQuery对象。如下:

1
$('#box').css('color', 'red').css('font-size', '50px'); //连缀

二. 加载模式

我们在之前的代码一直在使用$(function () {});这段代码进行首尾包裹,那么为什么必须
要包裹这段代码呢?

  • 原因是我们jQuery库文件是在body元素之前加载的,我们必须等待所有的DOM元素加载后,延迟支持DOM操作,否则就无法获取到。使用document.ready(),只需要等待DOM加载完成就执行。
  • 我们的原生Javascript也有一个延迟加载的方法onload,当网页内容全部加载完成后执行(例如图片等大文件未加载完成之前,JS功能处于假死状态)。
  • 下面来看看它们loadready区别到底在什么地方:
区别 window.onload $(document).ready()
执行时机 必须等待网页全部加载完毕(包括图片等),然后再执行包裹代码 只需要等待网页中的DOM结构加载完毕,就能执行包裹的代码
执行次数 只能执行一次,如果第二次,那么第一次的执行会被覆盖 可以执行多次,第N次都不会覆盖上一次
简写方案 $(function () {});

慕课网的DOM探索之基础详解篇 有对DOM Ready的一些介绍。

三. 对象互换及处理多个库之间的冲突

1. 对象互换。

首先我们来看一下这段代码:

1
2
3
4
alert($);//返回jQuery对象方法内部函数
alert($());//[object object],返回jQuery对象
alert($("#box"));//[object object],返回jQuery对象
alert(document.getElementById("box")); //[object HTMLDivElement],返回原生DOM对象

如何进行转换呢?
jQuery 想要达到获取原生的 DOM 对象,可以这么处理:
alert($('#box').get(0)); //ID 元素的第一个原生 DOM

从上面 get(0),这里的索引看出,jQuery 是可以进行批量处理 DOM 的,这样可以在很
多需要循环遍历的处理上更加得心应手。

当然要重新转化成jQuery对象的话,只需要使用$()包裹原生对象就可以了。

2. 多个库之间的冲突

当一个项目中引入多个第三方库的时候,由于没有命名空间的约束(命名空间就好比同
一个目录下的文件夹一样,名字相同就会产生冲突),库与库之间发生冲突在所难免。

jQuery 只不过是 DOM 操作为主的库,方便我们日常 Web 开发。但有时,我们的项目有更多特殊的功能需要引入其他的库,比如用户界面 UI 方面的库,游戏引擎方面的库等等一系列。

所以jQuery提供了一个方法:jQuery.noConflict();:将$符所有权剔除。

1
2
3
4
5
6
<script src="other_lib.js"></script>
<script src="jquery.js"></script>
<script>
jQuery.noConflict();
// 现在就$所有权就不归jQuery了。
</script>

同时还可以使用

var $$ = jQuery;:这样$$,就完全实现了原来$函数的功能。

一个前端程序猿的Sublime Text3的自我修养

详细设置 && 20+插件

本文章会在本人有插件或者设置更新时,进行不定时更新

2015-12-31更新:NO. 21 侧边栏同步编辑窗口底色插件。
2016-04-06更新:代码片段:better-completions;主题:Material,Seti_UI;代码格式化:HTML-CSS-JS Prettify;vue语法高亮:Vue Syntax HighlightTerminalMarkdownEditing个性化定制
GitSavvy 、
2016-07-03更新:自定义代码片段;livereload:保存自动刷新浏览器;WakaTime:记录你的编程时间

为什么要选择Sublime Text3?

  • Sublime Text3 自动保存,打开图片
  • 跨平台启动快!!!!多行游标,太好用。
  • 插件,简直选不过来。
  • 代码片段
  • VIM兼容模式

菜单栏基础功能介绍

菜单栏介绍

  1. File:文档相关,新建文件,打开文件或文件夹等。
  2. Edit:文件编辑相关,复制,剪切等(CVS大法好)。除此之外还有一些强大的功能。
  3. Selection:选择相关,帮助选择代码。
  4. Find:查找替换相关。这个和其它编辑器区别好像不大。
  • Ctrl+F查找、Ctrl+H替换等。
  1. View:对Sublime_Text编辑器本身的一些配置。
  • SideBar:开启侧边栏Ctrl+k,b
  • Show console:打开控制台窗口,安装package control需要使用.
  1. Goto:快捷导航:下面介绍。Goto Anything
  2. tools:工具,一些命令。
  • new Snippet:自定义代码片段,保存到user下
  1. Project: 项目相关,用的少。
  2. Preferences:对于sublime_text进行一些个性化定值。
  3. Help:如同名字。注册在这里

快捷键

  • line相关:
  • Ctrl+Shift+D:复制当前行
  • Ctrl+Shift+K:删除当前行
  • Ctrl+j 合并一行
  • Ctrl+Enter:在当前行下添加新行。After
  • Ctrl+Shift+Enter:在当前行上添加新行。Before
  • Ctrl+Shift+上、下:移动当前行
  • Comment注释:
  • Ctrl+/:行注释。
  • Ctrl+Shift+/:块注释
  • Ctrl+Shift+P:调用命令面板,快速查找,例如:改变语法模式等。
    • 模糊匹配,可以减少对快捷键的记忆。
  • Shift+Alt+1,2,3,4,5:开启对应数字的多栏编辑

Ctrl+P:Goto Anything

  • Ctrl+P: 查找项目中的文件:
  • 直接输入名称:在不同文件中切换,支持级联的目录模式
  • ::+ 行号:Ctrl+G 定位到具体的行。
  • @:+ 符号:Ctrl+R定位到具体的符号,例如:JS函数名,CSS选择器名。
  • #:+ 关键字:Ctrl+;匹配到具体的匹配的关键字。主要是模糊匹配。

多行游标

  • Ctrl+D:选中当前光标所在位置的单词。连续使用时,进行多光标选择,选中下一个同名单词。
  • Ctrl+K:配合Ctrl+D可以跳过下一个同名单词。
  • Ctrl+L:选择当前光标所在位置的。连续使用时,继续选中下一行。
  • Ctrl+Shift+L:在多行选中后,在所有选中的行后产生游标。
  • Alt+F3:选中文档中所有的同名单词。
  • Shift+鼠标右键:向下拖动,产生多个光标。

设置

使用 View–>Show console,快捷键: Ctrl+` 调出console面板输入sublime.log_commands(True),可以得到当前使用的命令面板进行设置的值。方便进行快捷键的绑定。

下面这些都可以通过命令面板快捷查找

  • Settings-User:个人对于sublime_text的定制。使用JSON格式,会直接覆盖掉Settings-Default默认设置中的内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// User/Preferences.sublime-settings
//我觉得自带字体挺好的。
{
"color_scheme": "Packages/Material Theme/schemes/Material-Theme-Darker.tmTheme",//主题,
"theme": "Material-Theme-Darker.sublime-theme",//侧边栏样式,需要安装该主题才可以使用
"draw_minimap_border": true, // 右侧缩略图边框
"font_face": "Monaco",//字体
"font_size": 10, // 字体大小
"highlight_line": true, // 当前行标亮
"save_on_focus_lost": true, // 当前行标亮
"theme": "Spacegray Eighties.sublime-theme", //主题相关
"word_separators": "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?", // 双击选中中划线
"word_wrap": true, //自动换行
"trim_trailing_white_space_on_save": true, //自动移除行尾多余空格
"ensure_newline_at_eof_on_save": true, //文件末尾自动保留一个空行
"disable_tab_abbreviations": true, //禁用 Emmet 的 tab 键功能(请使用 ctrl+e)
"translate_tabs_to_spaces": true, //把代码 tab 对齐转换为空格对齐
"tab_size": 4, //空格数
"fade_fold_buttons": false, //显示代码块的倒三角
"bold_folder_labels": true, //侧边栏文件夹加粗
"auto_find_in_selection": true //开启选中范围内搜索
}
  • key - Bindings-User:个人对于快捷键的设置。同样会覆盖默认的设置。例如:
1
2
3
4
//自动改变缩进格式
{
"keys": ["shift+tab"], "command": "reindent","args":{"single_line":false}
}

构建系统实现快捷调用浏览器

构建系统可以让您通过外部程序来运行文件,并可以在Sublime Text查看输出。

tools:工具下的Build System选择新建一个选项后(Build System–>New Build System),进行如下设置(注意后缀),保存到user目录下:

1
2
3
4
5
//这样设置。。地址是你的浏览器位置
{
"cmd" :["C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe","$file"],
"selector":["text.html"]
}

之后再进入刚刚的的地方,选择第一个Automatic,修改内容后按Ctrl+B,可以看到自动调用chrome并且是修改后的内容。在sublime Textconsole中可以看到输出的信息。更多功能请查看这里

上面是一些基础功能的介绍

2016-07-03 更新:自定义sublime代码片段

我们在开发中有很多代码是需要重复编写的,每一次都去复制粘贴显然是一件效率极其低下的事情,sublime的自定义代码片段功能就很好的解决了这个问题。下面就来看一下如何在sublime中自定义代码片段

首先在菜单栏选择:Tools ->developer -> New Snippet可以看到新建一个xml类型的描述文件,如下:

1
2
3
4
5
6
7
8
9
<snippet>
<content><![CDATA[
Hello, ${1:this} is a ${2:snippet}.
]]></content>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<!-- <tabTrigger>hello</tabTrigger> -->
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<!-- <scope>source.python</scope> -->
</snippet>

注释已经非常详细了,content 里面就是代码模版:${序号:默认值} ,序号相同的地方光标会同时停在那可以多处同时编辑。序号大小就是 tabindex。在实际使用代码的时候,可以使用 tab 切换光标位置。

以hexo新建一篇博客头部为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<snippet>
<content>
<![CDATA[
title: ${1:标题}
date: ${2:2015-12-26 15:26:17}
tags: [${3:标签}]
categories: [${4:分类}]
]]>
</content>
<!-- 可选:快捷键,利用Tab自动补全代码的功能 -->
<tabTrigger>hexoH</tabTrigger>
<!-- 可选:使用范围,不填写代表对所有文件有效。附:source.css和test.html分别对应不同文件。 -->
<!-- <scope>source.md</scope> -->
<!-- 可选:在snippet菜单中的显示说明(支持中文)。如果不定义,菜单则显示当前文件的文件名。 -->
<description>hexo博客头部生成</description>
</snippet>

设置完毕,最后还差一步,要想代码片段生效,还必须保存到sublime的文件目录\Data\Packages\User,文件名任意,但文件后缀必须为.sublime-snippet

现在输入hexoH试试,你想要的代码片段是不是已经有了呢?

插件的安装与使用

安装package control

这里我使用的是sublimeText 3,2 的话上官网查询代码。
首先打开package control官方网站
复制下面这一段代码:

1
import urllib.request,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

在上面说的View–>Show console,快捷键: Ctrl+` 打开控制台窗口,粘贴上面的代码,回车,然后就是等待安装了,需要一定的时间。安装完成后重启 。

使用Ctrl+Shift+P,打开控制面板,输入PC,效果如下:说明安装成功了。

package control

安装主题

  • 按照上面的步骤,打开图片中的安装插件就行了,其实默认配色真的挺好看的
  • 推荐在安装前,先去官方网站查看样式。的样式,以及设置方法,说明文档。一般安装成功后,会自动弹出。
  • Theme - Spacegray为例:

Theme - Spacegray
先使用Ctrl+Shift+P 输入PCI,回车选择 Install Package 。需要等待一会加载时间,输入Theme-Spacegray。其实不输入完也会模糊匹配出来的。

  • 回车等待安装就好,成功后会弹出一个使用设置的页面,把其中的如下代码拷贝到Settings-User,保存,你会发现,默认的主题已经变成了刚刚我们查看过的主题了。
1
2
"color_scheme": "Packages/Theme - Spacegray/base16-eighties.dark.tmTheme",
"theme": "Spacegray Eighties.sublime-theme"

当然,你也可以通过菜单栏,进行主题的选择。会有相同的效果。它会自动在Settings-User进行设置。

2016-04-6 更新

安利两款主题:

两款主题都有侧边栏图标显示:我在这里说不清到底谁好谁差,全凭个人的喜好吧!

  1. Material:

Theme_Material

  1. Seti_UI:

Theme_Seti_UI

安装方法还是和上面一样,最好是根据它的README描述来进行设置。

个人常用插件及使用方法:

NO.1 AdvancedNewFile:快速新建文件。

  • 假设有文件夹file。我们正在输入代码,又想在新的子目录下新建html文件的话用传统方式得很多步,新建目录,新建文件,保存等等等。
  • 但是有了该插件之后,事情就变得简单了许多,只需要按下Ctrl+Shift+N,输入文件夹以及文件名,你就会看到如下效果:(回车,你会发现已经子目录下的文件已经新建完成了!)

AdvancedNewFile

NO.2 Nettuts+ Fetch:管理类库。

安装成功后输入Ctrl+Shift+P打开命令面板,输入Fetch,可以看到以下:

Nettuts+ Fetch
选择file可以看到设置的文件。选择下载
配合刚刚上面的插件使用,简直完美..

NO.3 Sidebar Enhancements:增强侧边栏。

必装插件,无比强大,就不过多介绍了。可以在浏览器中打开,还可以配置不同文件的打开方式。
单单下面这一个功能就必须安装了!快捷在不同浏览器打开:

配置设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[
{
"keys": ["f1"],
"command": "side_bar_files_open_with",
"args": {
"paths": [],
"application": "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",//你的浏览器路径
"extensions": ".*" //匹配任何文件类型
}
},
{//firefox
"keys": ["f2"],
"command": "side_bar_files_open_with",
"args": {
"paths": [],
"application": "D:\\浏览器\\火狐\\firefox.exe",
"extensions": ".*"
}
},
{ //ie
"keys": ["f3"],
"command": "side_bar_files_open_with",
"args": {
"paths": [],
"application": "C:\\Program Files\\Internet Explorer\\iexplore.exe",
"extensions": ".*"
}
}
]
  • 可选SyncedSideBar:每次打开文件,侧边栏都会同步显示该文件所在目录树中的位置

NO.4 Doc​Blockr:代码块注释。

可以快速的对函数进行注释。保持代码规范。支持多种语言。(个人觉得brackets的这个插件比Sublime Text做得好多了。)

  • /*:回车创建一个代码块注释
  • /**:回车在自动查找函数中的形参等等。

它会生成 JSDoc 格式的注释。如果你从没有使用过类似的工具,DocBlockr 会让你觉得以前没有它是如何写代码的。帮助你创造你的代码注释,通过解析功能,参数,变量,并且自动添加基本项目。

NO.5 SublimeLinter-jshint:语法校验

  • 需先安装SublimeLinter
  • 需先安装Node.JSnpm
  • 在cmd输入 npm install -g jshint,等待安装成功就好了。

安装成功后,重启就可以测试代码的风格了。
当然还可以自定义校验规则,在该目录下使用Ctrl+Shift+N创建文件.jshintrc,在其中使用JSON格式配置校验风格。

例如:

1
2
3
4
//建议使用===,不使用时会有提示。
{
"eqeqeq":true
}

并且在左下角会有错误提示。需要注意的是内容有更改时,才会立即生效。
详细自定义规则:自定义Hint校验

NO.6 Git :版本控制

可视化的操作:帮助你与你的Git repo协议进行交互。它支持很多命令像init, push, pull, branch, stash,等等。了解更多关于你在Sublime Text里面究竟能使用哪些Git功能,以提高您的工作流程。
使用参考

  • GitGutter:
    Sublime Text 有了 Git 插件之后,GitGutter 更好的帮助开发者查看文件之前的改动和差异,提升开发效率。(其实我是冲着这个来的)

NO.7 Emmet:不解释。

中文文档:地址

前端开发必备!Emmet使用手册

NO.8 JsFormat:代码格式化 使用 HTML-CSS-JS Prettify,下面介绍:

  • JsFormat 基于 JS Beautifier,可以帮助你自动格式化JavaScript和 JSON。这对于阅读代码是非常有用的。
  • 快捷键:Ctrl + Alt + f 或者,你也可以使用菜单栏。
  • 可定制喜欢的格式:在 SublimeText 3 中 Preferences -> Package Settings -> JsFormat -> Settings - Default 可以调整这些配置。

NO.9 jQuery:jQuery的API代码片段

我知道目前在很多地方 jQuery 看似已经落伍了,但是如果你不是建立一个交互性很强的网站或者你只是想在已有应用上添加功能,它仍然是非常有用的。

比如,输入 $.a就可以让我选择$.ajax(),然后自动扩展成以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$.ajax({
url: '/path/to/file',
type: 'default GET (Other values: POST)',
dataType: 'default: Intelligent Guess (Other values: xml, json, script, or html)',
data: {param1: 'value1'},
})
.done(function() {
console.log("success");
})
.fail(function() {
console.log("error");
})
.always(function() {
console.log("complete");
});

NO.10 BracketHighlighter:符号高亮

该插件提供行数列高亮的各种配对的语法符号,显示在行号上。效果如下:

BracketHighlighter

配置方法参考sublime text3下BracketHighlighter的配置方法

NO.11 JavaScript Next:完美支持ECMAScript 6

  • JavaScript Next 提供了比默认JavaScript Package更好的语法高亮,而且他完美支持ECMAScript 6。
  • 建议完全使用 JavaScript Next代替JavaScript Package。

NO.12 CSS3:CSS3语法高亮

  • 默认安装的Sublime Text对CSS3的支持让人抓狂,帧动画?别开玩笑了你只会看到一片白色的纯文本一样的代码。
  • 事实上不光CSS3,我建议用CSS3 Package完全替代原来的CSS Package来完成语法高亮。把原来的禁用了吧

NO.13 Color Highlighter :CSS颜色高亮

  • 这个插件我等了很久了(在使用breakets的时候发现 的,好用到爆),我最早用Sublime Text写CSS时候就在想“这堆颜色码谁知道是什么颜色”。。
  • 还是brackets的牛逼
  • Color Highlighter这个插件会检测CSS文件中的颜色码,不论是Hex码或者RGB码都能很好的显示。
  • Color Highlighter能够设置成用背景色或者边框提示颜色,我一般在Settings里做这样的设置:
1
2
3
4
{
"ha_style": "filled",
"icons": false
}

效果如下:

Color Highlighter

NO.14Colorpicker:使用一个取色器改变颜色

使用方法: ctrl + shift + c,快捷键有冲突,需修改。可以通过ctrl+shift+p:搜索Colorpicker调用

Colorpicker

NO.15 Markdown EditingMarkdown Preview,实现预览MD

  • 当在 Sublime Text 中编写 markdown 文件时,在浏览器中打开全是乱码,因为还没有将 markdown 文件解析成相应的 HTML.
  • 这两个插件的功能就是可以用浏览器浏览 Sublime Text 中编写的 markdown文件。
  • 配置:

打开 Preferences->Package Settings->Markdown Preview->Setting User 将下面这句话粘贴进去。

1
2
3
4
5
6
7
8
9
{
// "浏览markdown的浏览器的路径"
"browser" : "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
}
//打开Preferences->Key Binding User,添加下面一句话。
{
"keys": ["f6"], "command": "markdown_preview", "args": {"target": "browser", "parser":"markdown"}
},
//keys的值是以上面浏览器预览markdown文件。

直接按F6就可以打开浏览器预览markdown,并且它们的安装还会让编写markdown时支持一些快捷键。

不进行这些配置的话,因为我们在前面 构建系统 使用了一些操作,按ctrl+b,就会在当前文件目录下,创建一个同名的html文件。

选中该htnl文件,再次按ctrl+b可以达到同样的预览效果,不过还是F6简单不是吗?

2016-04-06 更新

我知道你们都忍不了那默认的白色背景,丑爆了好吗? 现在我们找到
preference–>package Settings–>MarkdownEditing–>Markdown GFM Settings-users,把下面这个复制进去:

1
2
3
{
"color_scheme": "Packages/Material Theme/schemes/Material-Theme-Darker.tmTheme",
}

然后你就会发现世界又回到了春天-。-(这里使用的我上面介绍的Material),更多的设置就举一反三啦!

NO.16 AutoFileName:文件路径自动提示

这个直接安装就可以用了,挺方便的。

NO.17 Terminal:在Sublime Text直接打开命令行

2016-04-06 更新

默认快捷键 Ctrl+Shift+T

windows下默认会打开Windows PowerShell,那界面简直丑到不行好吗!!

根据上面的经验同样找到preference–>package Settings–>Terminal–>Terminal Settings-users:进行下面的设置:

1
2
3
4
5
{
"terminal": "C:\\WINDOWS\\system32\\cmd.exe",
//"terminal": "C:\\Program Files\\cmder\\Cmder.exe",
"parameters": ["/START", "%CWD%"]
}

然后人生就焕发了第二春 -。-(强烈建议大家去搜索使用被我注释掉的**Cmder**,这才是一个shell的样子嘛!)

NO.18 CSScomb : CSS属性排序

NO.19 JavaScript CompletionsJava​Script & Node​JS Snippets。输入提示,代码补全

  • 看个人喜好咯,不用代码补全,可以锻炼英语!!

NO.20 SyncedSidebarBg:自动同步侧边栏底色为编辑窗口底色。

有人反映说安装主题后侧边栏颜色不更改,其实有两个方法,一个在\Data\Packages\主题修改主题配置。

比较麻烦,就不说了,直接安装这个插件就好了,记得重启刷新。

2016-04-06 更新

NO.21 HTML-CSS-JS Prettify: HTML-CSS-JavaScript 代码格式化

其实有了这个代码格式化插件,就可以删除上面的代码格式化插件了。因为功能确实强大!

其实我把官网的配置趴下来之后就改了两个地方:

  • "selector_separator_newline": false: 不需要每个CSS选择器单独占一行
  • "allowed_file_extensions": ["..这是老的,新增在-->","vue"],:将vue的组件当成html来进行格式化
  • 默认快捷键:Ctrl+Shift+H

更多的个性化定制大家自己去实现吧!

NO.22 better-completions: 涵盖了html, jqueryjavascriptBootstrap的代码片段。

官网介绍的很详细。
这个插件要是包含的类型多,当然在每一种语言上的匹配肯定是不如上面介绍。不过为了少按几个插件,还是用了它,把上面的几个代码片段插件删了。
不过它也是支持加载自定义代码片段的,如果有需要的话,那就自己编写吧^_^。

NO.23 liveReload:文件保存浏览器即时刷新!

该插件在window下,有很多问题会导致不能使用,mac下可以正常使用

  • 需安装对应的chrome插件:chrome商店下载,完成后需勾选允许访问文件网址
  • 为了避免每一次启动实时刷新在sulime里面启动一遍插件,可在插件设置中增加如下字段:
1
2
3
4
5
6
{
"enabled_plugins": [
"SimpleReloadPlugin",
"SimpleRefresh"
]
}

这时就只需要在浏览器端点一下小圆圈就好了

其他:

  • SideBar Folders:更好的管理侧边栏文件夹
  • IMESupport :输入法不跟随时安装
  • FileHeader :自动更新保存时间,文件模板
  • Quote​HTML :把HTML拼接成js插入字符串,字符串拼接的痛
  • CSS Format :CSS格式化
  • AutoPrefixer :浏览器私有属性前缀补全 (Node.js依赖)
  • ConvertToUTF8:GBK编码兼容
  • Vue Syntax Highlightvue文件的语法高亮
  • WakaTime:记录你的编程时间
  • rem-unit:px单位自动转rem,移动端开发必备

参考如下:

task0002(四)- 练习:数据处理、轮播及交互

包括5部分:

  • 小练习1-处理用户输入
  • 小练习2-日期对象的使用
  • 小练习3:轮播图
  • 小练习4:输入提示框
  • 小练习5:界面拖拽交互

源码地址
task0002 在线Demo

小练习1:处理用户输入

这里直接可以利用原来写过的util.js。所以应该不是特别难。主要考察对字符串的操作,以及正则表达式的使用

任务描述

task0002目录下创建一个task0002_1.html文件,以及一个js目录和css目录,在js目录中创建task0002_1.js,并将之前写的util.js也拷贝到js目录下。然后完成以下需求。

第一阶段

在页面中,有一个单行输入框,一个按钮,输入框中用来输入用户的兴趣爱好,允许用户用半角逗号来作为不同爱好的分隔。

当点击按钮时,把用户输入的兴趣爱好,按照上面所说的分隔符分开后保存到一个数组,过滤掉空的、重复的爱好,在按钮下方创建一个段落显示处理后的爱好。

实现

根据题目要求,这个地方比较简单,可以直接利用前面写过的一些函数,分成四步,进行。

  1. 使用正则表达式来对字符串进行分割操作。
  2. uniqArray函数进行去重操作。
  3. for循环,trim函数对数组项进行去除首尾空格,用来处理,该项为空的情况。
  4. innerHTML进行输出。

html:

1
2
3
<input type="text" id="user_input">
<button id="btn">提交</button>
<ul id="user_output"></ul>

task0002_1.js中的js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//自执行的匿名函数
(function handle_1() {
//其实这里没必要使用id因为数据那么少$函数直接获取标签就行
var inp = $("#user_input");
var out = $("#user_output");
$.click("#btn", function () {
var value = inp.value.split(/\,|\,/); //1.根据半角逗号分割成数组。
var unValue = uniqArray(value); //2.数组去重
for (var i = 0, len = unValue.length; i < len; i++) {
var trimValue = trim(unValue[i]); //3.对每一项进行去除首尾空格操作
console.log(trimValue);
if (trimValue !== "") { //4.只有在去除首尾空格后不为空的数组才输出。
out.innerHTML += "<li>" + trimValue + "</li>"
}
}
})
})();

第二阶段

单行变成多行输入框,一个按钮,输入框中用来输入用户的兴趣爱好,允许用户用换行、空格(全角/半角)、逗号(全角/半角)、顿号、分号来作为不同爱好的分隔。

当点击按钮时的行为同上

实现

看题目描述,主要是对于第一步进行修改,第一阶段只要求对半角逗号进行处理,但是在第二阶段中,需要对“换行、空格(全角/半角)、逗号(全角/半角)、顿号、分号”进行处理。
主要是考察对于正则表达式的应用。

只需要对var value = inp.value.split(/\,|\,/); 进行更改如下:

var value = inp.value.split(/\n|\s+|\,|\,|\、|\;|\;/);
需要注意的是:在正则表达式进行匹配这些符号时最好是前面加上转义字符。

第三阶段

用户输入的爱好数量不能超过10个,也不能什么都不输入。当发生异常时,在按钮上方显示一段红色的错误提示文字,并且不继续执行后面的行为;当输入正确时,提示文字消失。

同时,当点击按钮时,不再是输出到一个段落,而是每一个爱好输出成为一个checkbox,爱好内容作为checkbox的label。

实现

  • 嗯,其实这里按照题目要求应该要实时监听输入值变化,但是那样太麻烦了,,所以就直接在点击按钮的时候判断了。
  • 直接判断数组长度就行了。。输入为空时,判断字符串=“”。
  • 输出checkbox这里不过多的设置了,只是演示。

最终完成

html:

1
2
3
4
5
<textarea name="user_input" id="user_input" cols="45" rows="10"></textarea>
<br>
<button id="btn">处理并输出</button>
<p>输入的爱好数量不能超过10个,或什么都不输入</p>
<form id="user_output"></form>

js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(function handle_1() {
var inp = $("#user_input");
var out = $("#user_output");
$.click("#btn", function () {
var value = inp.value.split(/\n|\s+|\,|\,|\、|\;|\;/); //分割成数组。
var unValue = uniqArray(value); //数组去重
var i = 0;
var len = unValue.length;
if (len > 10 || unValue == "") {
$("p").style.disautoPlay = "block";
} else {
$("p").style.disautoPlay = "none";
for (; i < len; i++) {
var trimValue = trim(unValue[i]); //对每一项进行去除首尾空格操作
console.log(trimValue);
if (trimValue !== "") { //只有在去除首尾空格后不为空的数组才输出。
out.innerHTML += "<label>" + "<input type='checkbox'>" + trimValue + "</label>"
}
}
}
})
})();

在线演示:**小练习1:处理兴趣列表**

小练习2:日期对象的使用

任务描述

在和上一任务同一目录下面创建一个task0002_2.html文件,在js目录中创建task0002_2.js,并在其中编码,实现一个倒计时功能。

  • 界面首先有一个文本输入框,允许按照特定的格式YYYY-MM-DD输入年月日;
  • 输入框旁有一个按钮,点击按钮后,计算当前距离输入的日期的00:00:00有多少时间差
  • 在页面中显示,距离YYYY年MM月DD日还有XX天XX小时XX分XX秒
  • 每一秒钟更新倒计时上显示的数
  • 如果时差为0,则倒计时停止

实现思路

了解日期对象

这里主要是考察的对于日期对象的使用。

new Date()。如果没有输入任何参数,则Date的构造器会依据系统设置的当前时间来创建一个Date对象。表示当前系统时间。

1
2
3
4
5
6
//时间对象创建的几种方式。
var today = new Date();
var birthday = new Date("December 17, 1995 03:24:00");
var birthday = new Date("1995-12-17T03:24:00");
var birthday = new Date(1995,11,17);
var birthday = new Date(1995,11,17,3,24,0);
  • Date对象中处理时间和日期的常用方法:详细内容在MDN

日期对象常用方法

正式开始:

  1. 创建时间处理函数,使用正则表达式,处理输入的值,value.match(/(^\d{4})-(\d{2})-(\d{2}$)/);用到了match方法和正则的分组,在我写的正则博客里应该有过详细介绍了。这里也不过的说明了。
  2. 使用目标时间的getTime()毫秒数减去得到当前的毫秒数,得到相差的毫秒数。处理它:(注意毫秒的问题)
  • (60 * 60 * 24) :剩余的天数。
  • (60 * 60) % 24) :剩余的小时数。依次类推。
  1. innerHTML输出,并且判断相差时间,改变输出的值。
  2. 定时器的使用。(我这里使用的setTimeout(),使用递归调用实现自执行)
  • 计时器setTimeout(函数,延迟时间);,在载入后延迟指定时间后,去执行一次表达式,仅执行一次。
  • 取消计时器:clearTimeout()停止计时器。
  1. 给按钮添加点击事件,在点击时,调用刚刚编写的的时间处理函数。

在线演示:**小练习2:倒计时**

小练习3:轮播图组件

任务描述

在和上一任务同一目录下面创建一个task0002_3.html文件,在js目录中创建task0002_3.js,并在其中编码,实现一个轮播图的功能。

  • 图片数量及URL均在HTML中写好
  • 可以配置轮播的顺序(正序、逆序)、是否循环、间隔时长
  • 图片切换的动画要流畅
  • 在轮播图下方自动生成对应图片的小点,点击小点,轮播图自动动画切换到对应的图片

效果示例:http://echarts.baidu.com/ 上面的轮播图(不需要做左右两个箭头)

实现思路:

主要是对于考察对于定时器,以及事件绑定的处理,以及动画效果,为此我专门写了一篇博客《JS完美运动框架的封装过程》。 这里就直接使用里面封装好的函数了。

本来都要放弃组件的编写了,只想按照常规方法来写个轮播图就好,但是在先把小练习4完成的情况下,就发现了一种新方法,可以帮助我完成这个任务(强烈建议先看小练习四!)使用nextElementNode,图片向左切换。(不涉及任何css,我假设css都会了,也没有用到特别难css属性)

该轮播图有依赖函数。主要使用到以下函数:

  • 选择器函数$(class);
  • 运动框架startMove
  • 同时在该函数引入时,还依赖于获取实际样式函数getStyle
  • 获取当前元素在同级元素的索引getIndex;
  • 事件代理函数:delegateEvent
  • 添加class与删除class元素addClass、romoveClass

第一步:实现点击切换

编写幻灯片函数:Slideshow(element)

  1. 根据图片的数量创建与图片数量相同的导航小点:ul>li*length 。设置其li样式,并且默认把第一个li设置为活动状态classNameactive
  2. 编写点击函数clickLi
  3. li添加事件代理函数。
  4. 据点击的li的索引值算出来动画的目标值,-iCurrent * getIndex(this);
  5. 移除所有li上的选中状态active:编写函数removeLiClass(),在后面我们还要用到它。
  6. 设置当前点击的li为选中:状态active
  7. 调用运动框架实现动画效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function Slideshow(element) {
//1.创建li
var imgArr = element.getElementsByTagName("img"); //获取图片数量
var imgArrLen = imgArr.length; //缓存图片数量
var createUl = document.createElement("ul"); //创建小点的ul
var iCurrent = parseInt(getStyle(imgArr[0], "width")); //获取一张图片的宽度
element.style.width = iCurrent * imgArrLen + "px"; //设置图片容器的宽度。
//创建li
for (var i = 0, len = imgArrLen; i < len; i++) {
createUl.innerHTML += "<li></li>";
}
element.parentNode.appendChild(createUl); //插入导航
addClass(createUl, "Slideshow-nav"); //添加导航样式
addClass(createUl.getElementsByTagName("li")[0], "active"); //默认设置第一个为第当前活动的li

//编写点击函数clickLi:
clickLi();
/**
* 点击导航
*/
function clickLi() {
delegateEvent(createUl, "li", "click", function () {
var iTaget = -iCurrent * getIndex(this);
removeLiClass();
addClass(this, "active"); //移出
startMove(element, {
"left": iTaget
});
});
}

/**
* 用于移除所有的Li的选中状态:active
*/
function removeLiClass() {
var oLi = createUl.getElementsByTagName("li");
for (var i = 0, len = oLi.length; i < len; i++) {
removeClass(oLi[i], "active");
}
}
}

第二步:实现自动播放

为了方便后续的封装,暂时只考虑:正序不循环的情况!

  1. 创建自动播放函数paly(),获取当前为选中状态active的li.
  2. 设置目标值,根据选中状态的索引+1 *width来设置。(注意是负值,同时考虑索引值+1为length的情况。)
  3. 因为不循环,需要在设置(getIndex(heightLi)+1)===imgArrLen-1清除定时器。(其实就是轮播到最后的时候,至于为什么是这样,可以自己研究一下,更改一下值。)
  4. 获取下一个元素节点,存在的话,取消现有选中状态,设置下一个元素节点为选择中,调用运动框架!实现动画,
  5. 添加定时器setInterval(),调用该函数,实现自动播放。
  • 测试几次,你会发现:点击li和自动播放之间存在冲突,动画效果都没做完就播放下一张了,如何解决呢?
  1. 给图片容器添加一个鼠标移入和移除事件(mouseover、mouseover)也就是hover上去的效果
  • 移入时,清除定时器,暂停播放。
  • 移出时,开启定时器,继续轮播。
  • 然后你会发现问题成功的解决了!(而且我观察了大部分的轮播,都是这个效果,移入暂停,移出继续)

这里我们的最基本的功能都实现了,代码如下:(放在Slideshow()函数内部)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var iSpeed = 4000;//定时器间隔。

hoverElement();
/*
* 移入图片容器暂停,移除继续播放。
*/
function hoverElement() {
addEvent(element.parentNode, "mouseover", function () {
clearInterval(timer);
});
addEvent(element.parentNode, "mouseout", function () {
timer = setInterval(autoPlay, iSpeed);
});
}

var timer = null;
timer = setInterval(autoPlay, iSpeed);
/*
* 自动播放函数。
*/
function autoPlay() {
var heightLi = $(".Slideshow-nav .active"); //高亮的li
var iTaget;
iTaget = (getIndex(heightLi) + 1) === imgArrLen ? 0 : (-iCurrent * (getIndex(heightLi) + 1));

if (getIndex(heightLi) + 1 === imgArrLen - 1) {
clearInterval(timer);
}
var nextLi = heightLi.nextElementSibling;
if (nextLi) {
removeLiClass();
addClass(nextLi, "active");
}
startMove(element, {
"left": iTaget
});
}

第三步:添加配置项(题目要求完成)

  1. 获取题目要求:主要是以下三点。
  2. 是否循环,默认为循环。
  3. 是否反向,默认不反向。只有循环时,才可反向。
  4. 轮播间隔时间,默认4000。
  5. 如何实现?(使用JSON如下:)
1
2
3
4
5
6
/*
* @param {JSON} option 配置项
* @config {String} noLoop 不循环?,默认为循环,只要存在则不循环,任意值
* @config {String} reverse 是否反向,任意值。只有“noLoop”不存在时,也就是只有循环时,才执行。
* @config {Number} intervalTime 轮播间隔时间(单位为毫秒),默认为4000,
*/
  1. Slideshow()内部,以下部分进行修改或添加。
  2. 先从简单的开始吧!判断option.intervalTime是否存在并且更改iSpeed的值(这样,轮播间隔时间配置就成功了),默认为4000毫秒。
  3. 改变自动播放(一):autoPlay函数。这里是整个改造中最复杂的部分!分几步进行。
  4. 把刚刚写的autoPlay函数内的内容,除去var heightLi = $(".Slideshow-nav .active"); var iTaget;这两个内容,其他的都使用if(option.noLoop){}包裹起来。
    这样就又完成了一个内容,当配置为不循环时的情况,就写好了。
  5. 既然有if不循环的情况,那么就肯定有else对应循环时的情况对吧?(笑)那么在里面应该怎么做呢?
  6. 第一次练习时,可以不添加函数,直接使用if else,对应正向与反向的情况!但是,在这里为了后面的进化,且不过多的阐述,就直接使用使用函数了,就叫他play(reverse)吧。
    在这里当然传入的参数是option.reverse
  7. 改变自动播放(二):编写play(reverse)
  8. 第一要务就是加入if eles啦,用来区分,true时为反向,false为正向(默认)。
  9. 先来说正向的情况!其实特别简单,有两步:
  • 第一步:删除(getIndex(heightLi)+1)===imgArrLen-1清除定时器的部分,只有这样才能实现循环,
  • 第二步:在上面被提取到不循环的内容中(也就是第一版的autoPlay())的if (nextLi)部分加上else的情况就行了!
    1
    2
    3
    4
    5
    //在下一个元素节点不存在的情况下(也就是到了最后了),设置第一个节点为活动状态,就这样正向的循环就成功了!
    else {
    removeLiClass();
    addClass($(".Slideshow-nav li"), "active");
    }
  1. 反向的情况(只需要对正向循环进行修改):
  • 改变目标值iTagetgetIndex(heightLi) === 0 ? -iCurrent * (imgArrLen - 1) : -iCurrent * (getIndex(heightLi) - 1);
  • 改变下一个元素节点,为前一个元素节点previousElementSibling
  • 改变上面else的情况,为设置最后一个节点为活动状态。代码就不贴了,有兴趣的看源码吧!
  1. 到现在函数就修改完成了。只需要在循环的情况下,调用该函数,并且传入option.reverse

其实到这里我们题目要求就完成了!

但是!不挑战一下怎么能行?

为什么不把左右点击切换一起实现了呢?

既然这样,我们就继续吧!

第四步:扩展!左右箭头实现!

  1. 创建并设置箭头样式(配合css使用)
    1
    2
    3
    4
    5
    //创建左右导航
    var createSpan = document.createElement("div");
    addClass(createSpan, "left-right")
    createSpan.innerHTML = "<span class='nav-left'>&lt;</span><span class='nav-right'>&gt;</span>"
    element.parentNode.appendChild(createSpan);
  2. 创建点击事件,事件代理。
  3. 炸裂的函数调用。
  • 还记得我们刚刚的play(reverse)函数吗?刚刚的功能完全不用封装函数,那为什么要做呢?就是这里啦!
  • 想想刚刚的函数实现了什么功能呢?是不是正向循环,和反向循环?,自动播放是因为在外层有定时器的缘故。
  • 所以只需要调用传参就行了!
  1. 传什么参数?
  • 想想左右箭头的索引,是不是0和1。这样就懂了吧!
  • 对索引进行取反。传参!代码如下:
1
2
3
4
5
6
delegateEvent(createSpan, "span", "click", function () {
var heightLi = $(".Slideshow-nav .active"); //高亮的待选li
var leftIndex = !getIndex(this); //点击左时为true,点击又为false
//移动的目标值,默认正向
play(leftIndex);
});

至此,我们的轮播图组件就完成了! 需要配合CSS使用。 通过这次封装,收益良多,感兴趣的话可以看看源码

在线演示:**小练习3:图片轮播组件**

小练习4:输入提示框

任务描述

在和上一任务同一目录下面创建一个task0002_4.html文件,在js目录中创建task0002_4.js,并在其中编码,实现一个类似百度搜索框的输入提示的功能。

要求如下:

  • 允许使用鼠标点击选中提示栏中的某个选项
  • 允许使用键盘上下键来选中提示栏中的某个选项,回车确认选中
  • 选中后,提示内容变更到输入框中

初级班:

  • 不要求和后端交互,可以自己伪造一份提示数据例如:
1
var suggestData = ['Simon', 'Erik', 'Kener'];

中级班:

  • 自己搭建一个后端Server,使用Ajax来获取提示数据

示例:

示例

实现思路

这里我并没有一开始就直接进行数据获取的部分,而是进行了任务分解,如下:

第一阶段

  1. 先在使用写好的ul>li标签下,。添加3个事件mouseovermouseoutclick。实现点击li使其值变成输入框内的值。(直接使用事件代理)
  2. 对输入框添加键盘事件(对,你没看错,只有在聚焦在输入框时才触发)
  • 获取当前高亮的li。没有则设第一个为高亮active
  • 判断keyCode使用键盘下键,使用nextElementSibling方法获取下一个节点(向上同理),取消当前的active,设置下一个为active。(使用键盘上下选中的效果,处理完成)
  • 判断keyCode,获取当前状态为active的值,实现回车时,把其设为input的值。
  1. 需要注意的地方:
  • 在移除高亮状态时,最好是遍历一遍。因为键盘与鼠标划过有可能同时触发,导致有多个高亮。
  • 错误处理

第二阶段

  1. 删除原来的ul>lihtml部分的li。添加对于输入框进行实时监听(这部分不在这里展开讲,如何实现大家去google吧,因为一展开就太多要说的了)。
  1. 使用AJAX获取服务器上的数据,解析,遍历,并进行数据匹配。
  2. 匹配成功显示ul,否则设为none
  3. 使用正则表达式的match方法,来获取匹配成功的把部分,使用span进行高亮显示。并且插入ul
  4. 改造第一阶段的函数:
  • 因为是使用的事件代理,直接对ul添加事件,所以需要修改的部分不是很多。
  • clickenter部分获取的值,因为span标签的存在,需要使用正则进行处理,输出删除span后的值。

在线演示:**小练习4:输入框即时提示**

小练习5:界面拖拽交互

  • 实现一个可拖拽交互的界面
  • 如示例图,左右两侧各有一个容器,里面的选项可以通过拖拽来左右移动
  • 被选择拖拽的容器在拖拽过程后,在原容器中消失,跟随鼠标移动
  • 注意拖拽释放后,要添加到准确的位置
  • 拖拽到什么位置认为是可以添加到新容器的规则自己定
  • 注意交互中良好的用户体验和使用引导

示例

实现思路:

第一步:封装拖拽函数

开一个DEMO页面,实践如下:

  1. 了解应该用到的事件,onmousedownonmousemoveonmouseup
  2. 思考对谁添加事件?
  3. 在鼠标点击div时,对div添加onmousedown,表示鼠标按下。
  4. 在事件内给document添加onmousemove。(为啥给document加呢?因为给div加在移动过快时会跳出去)表示鼠标移动。
  5. 并且添加document添加onmouseup,表示鼠标已经抬起,清除移动事件,以及本身。
  6. 思考如何设置对象的位置?
  7. 直接获取鼠标的位置并设置给div行不行呢?试试吧!显然,会出现问题,点击鼠标就到div左上角去了。
  8. 那么怎么改变呢?获取鼠标在div中的位置?对。就这样,在鼠标按下时记录鼠标在div中的位置.
  9. 在鼠标移动时,用当前的位置,减去刚刚的位置,这就是应该的值!
  10. 别忘了鼠标抬起时,需要清除事件,不然鼠标就粘住了。

    这里用到了event,事件对象的相关概念,推荐观看慕课网的视频。DOM事件探秘

  11. 善用this。

扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 鼠标拖拽函数。
* @param {HTMLElement} element 需要拖拽的对象
*/
function setDrag(element) {
addEvent(element, "mousedown", onmousedown);
//鼠标按下
function onmousedown(ev) {
var oEvent = ev || event;
var disX = oEvent.clientX - this.offsetLeft;
var disY = oEvent.clientY - this.offsetTop;
var that = this;
addEvent(document, "mousemove", onmousemove);
addEvent(document, "mouseup", onmouseup);
/**
* 鼠标移动
*/
function onmousemove(ev) {
var oEvent = ev || event;
that.style.left = oEvent.clientX - disX + "px";
that.style.top = oEvent.clientY - disY + "px"
}
/**
* 鼠标抬起删除事件
*/
function onmouseup() {
removeEvent(document, "mousemove", onmousemove);
removeEvent(document, "mouseup", onmouseup);
}
}
}

第二步:布局转换函数

  1. 两个参数,第一个参数,传入父级对象。第二个参数传入标签名,
  2. 循环,使用数组,对象,获取标签当前元素的位置lefttop。(offsetLeft)。这里不能使用获取实际样式函数,因为本身就需要获取其相对父元素的位置。
  3. 第二个循环
  • 设置lefttop值。
  • 设置绝对定位。
  • 取消原有的margin值。
  1. 调用函数,把对象从文档流布局,变成绝对定位布局。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 布局转换函数
* @param {HTMLElment} element HTML对象
* @param {string} childEle 其内需要转换的标签名
*/
function toPosition(element, childEle) {
var eleArr = element.getElementsByTagName(childEle);
var aPos = [];
//
for (var i = 0, len = eleArr.length; i < len; i++) {
aPos[i] = {
left: eleArr[i].offsetLeft,
top: eleArr[i].offsetTop
};
}
for (var i = 0, len = eleArr.length; i < len; i++) {
eleArr[i].style.left = aPos[i].left + "px";
eleArr[i].style.top = aPos[i].top + "px";
eleArr[i].style.position = "absolute";
eleArr[i].style.margin = "0";
}
}

在线演示:**小练习5:拖拽交互**

第三步:实现拖拽

前面我们已经实现了setDrag(element)函数,常规方法就是直使用循环,然后传入element

但是,为什么不用事件代理呢?

特别简单,只需要对上面写的函数进行一些简单的改装。

1
2
3
delegateEvent(parentElement, "li", "mousedown", function (ev) {
//此处是原函数中的内容。
}

现在知道为什么上面的函数会用到this了吧?

第四步:碰撞检测函数

先来看张图:
碰撞检测示意图

是不是有瞬间豁然开朗的感觉呢?

获取相关值,只需要考虑不碰不上的情况就行了!。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 碰撞检测函数
* @param {object} obj1 对象1
* @param {object} obj2 对象2
* @returns {boolean} 碰撞时返回true,否则反正false
*/
function hitDetection(obj1, obj2) {
//对象1的相关值
var l1 = obj1.offsetLeft;
var r1 = obj1.offsetLeft + obj1.offsetWidth;
var t1 = obj1.offsetTop;
var b1 = obj1.offsetTop + obj1.offsetHeight;
//对象2的相关值
var l2 = obj2.offsetLeft;
var r2 = obj2.offsetLeft + obj2.offsetWidth;
var t2 = obj2.offsetTop;
var b2 = obj2.offsetTop + obj2.offsetHeight;

if (r1 < l1 || l1 > r2 || b1 < t2 || t1 > b2) {
return false;//没碰上
} else {
return true;
}
}

第五步:处理各种碰撞情况

这里讲起来就太复杂了。源代码中注释还是比较详细的,有兴趣可以看下

JavaScript完美运动框架的进阶之旅

运动框架的实现思路

运动,其实就是在一段时间内改变leftrightwidthheightopactiy的值,到达目的地之后停止。

现在按照以下步骤来进行我们的运动框架的封装:

  1. 匀速运动。
  2. 缓冲运动。
  3. 多物体运动。
  4. 任意值变化。
  5. 链式运动。
  6. 同时运动。

(一)匀速运动

速度动画

运动基础

思考:如何让div动起来?
如下:

  1. 设置元素为绝对定位,只有绝对定位后,left,top等值才生效。
  2. 定时器的使用(动态改变值),这里使用setInterval()每隔指定的时间执行代码。
  • 计时器setInterval(函数,交互时间(毫秒)):在执行时,从载入页面后每隔指定的时间执行代码。
  • 取消计时器clearInterval(函数) 方法可取消由 setInterval() 设置的交互时间。
  1. 获取当前的位置,大小等等。offsetLeft(当前元素相对父元素位置)。
  2. 速度–物体运动的快慢
  • 定时器间隔时间
  • 改变值的大小

根据上面的信息我们就可以开始封装运动框架创建一个变化的div了。

1
2
3
4
5
6
7
8
9
10
/**
* 运动框架-1-动起来
* @param {HTMLElement} element 进行运动的节点
*/
var timer = null;
function startMove(element) {
timer = setInterval(function () {//定时器
element.style.left = element.offsetLeft + 5 + "px";
}, 30);
}

你没看错,就是那么简单。但是等等, what? 怎么不会停?WTF?

那是因为我们没有运动终止条件。好再还是比较简单。直接在定时器内部,判断到达目标值,清除定时器就行拉!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 运动框架-2-运动终止
* @param {HTMLElement} element 进行运动的节点
* @param {number} iTarget 运动终止条件。
*/
var timer = null;
function startMove(element, iTarget) {
timer = setInterval(function () {
element.style.left = element.offsetLeft + 5 + "px";
if (element.offsetLeft === iTarget) {//停止条件
clearInterval(timer);
}
}, 30);
}

就这样是不是就完成了呢?已经ok了呢?
no。还有一些Bug需要处理。

运动中的Bug

  1. 速度取到某些值会无法停止
  2. 到达位置后再点击还会运动
  3. 重复点击速度加快
  4. 速度无法更改

解决BUG

  1. 速度取到某些值会无法停止(这个Bug稍后解决,在进化过程中自然解决)
  2. 把运动和停止隔开(if/else)
  3. 在开始运动时,关闭已有定时器
  4. 把速度用变量保存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 运动框架-3-解决Bug
*/
var timer = null;
function startMove(element, iTarget) {
clearInterval(timer);//在开始运动时,关闭已有定时器
timer = setInterval(function () {
var iSpeed = 5;//把速度用变量保存
//把运动和停止隔开(if/else)
if (element.offsetLeft === iTarget) {//结束运动
clearInterval(timer);
} else {
element.style.left = element.offsetLeft + iSpeed + "px";
}
}, 30);
}

这样一个简单的运动框架就完成了。但是,再等等。只能向右走?别急,我们不是定义了把速度变成为了变量吗?只需要对它进行一些处理就行啦!
var iSpeed = 5;–>

1
2
3
4
5
6
7
//判断距离目标位置,达到自动变化速度正负
var iSpeed = 0;
if (element.offsetLeft < iTarget) {
iSpeed = 5;
} else {
iSpeed = -5;
}

透明度动画

  1. 用变量alpha储存当前透明度。
  2. 把上面的element.offsetLeft改成变量alpha
  3. 运动和停止条件部分进行更改。如下:
1
2
3
4
5
6
7
8
//透明度浏览器兼容实现
if (alpha === iTarget) {
clearInterval(time);
} else {
alpha += speed;
element.style.filter = 'alpha(opacity:' + alpha + ')'; //兼容IE
element.style.opacity = alpha / 100;//标准
}

(二)缓冲动画

思考:怎么样才是缓冲动画?

应该有以下几点:

  • 逐渐变慢,最后停止
  • 距离越远速度越大
  • 速度由距离决定
  • 速度=(目标值-当前值)/缩放系数
  • Bug :速度取整(使用Math方法),不然会闪
  • 向上取整。Math.ceil(iSpeed)
  • 向下取整。Math.floor(iSpeed)

还是对速度作文章:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 运动框架-4-缓冲动画
*/
function startMove(element, iTarget) {
clearInterval(timer);
timer = setInterval(function () {
//因为速度要动态改变,所以必须放在定时器中
var iSpeed = (iTarget - element.offsetLeft) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整
if (element.offsetLeft === iTarget) {//结束运动
clearInterval(timer);
} else {
element.style.left = element.offsetLeft + iSpeed + "px";
}
}, 30);
}
  • 做到这里,(速度取到某些值会无法停止)这个Bug就自动解决啦!
  • 例子:缓冲菜单
  • 跟随页面滚动的缓冲侧边栏
    在线演示:codepen

潜在问题目标值不是整数时

(三)多物体运动

思考:如何实现多物体运动?

  • 单定时器,存在问题。每个div一个定时器
  • 定时器作为对象的属性
    • 直接使用element.timer把定时器变成对象上的一个属性。
  • 参数的传递:物体/目标值
    比较简单把上面框架的进行如下更改:timer–>element.timer

就这样就行啦!

(四)任意值变化

咳咳。我们来给div加个1px的边框。boder :1px solid #000

然后来试试下面的代码

1
2
3
setInterval(function () {
oDiv.style.width = oDiv.offsetWidth - 1 + "px";
}, 30)

嗯,神奇的事情发生了!what?我设置的不是宽度在减吗?怎么尼玛增加了! 不对啊,大兄弟。

究竟哪里出了问题呢?

一起找找资料,看看文档,原来offset这一系列的属性都会存在,被其他属性干扰的问题。

好吧,既然不能用,那么我们就顺便把任意值变化给做了吧。

第一步:获取实际样式

使用offsetLeft..等获取样式时, 若设置了边框, padding, 等可以改变元素宽度高度的属性时会出现BUG..

  • 通过查找发现element.currentStyle(attr)可以获取计算过之后的属性。
  • 但是因为兼容性的问题,需封装getStyle函数。(万恶的IE)
  • 当然配合CSS的box-sizing属性设为border-box可以达到一样的效果 ? (自认为,未验证)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 获取实际样式函数
* @param {HTMLElement} element 需要寻找的样式的html节点
* @param {String]} attr 在对象中寻找的样式属性
* @returns {String} 获取到的属性
*/
function getStyle(element, attr) {
//IE写法
if (element.currentStyle) {
return element.currentStyle[attr];
//标准
} else {
return getComputedStyle(element, false)[attr];
}
}

第二步:改造原函数

  1. 添加参数,attr表示需要改变的属性值。
  2. 更改element.offsetLeftgetStyle(element, attr)
  • 需要注意的是:getStyle(element, attr)不能直接使用,因为它获取到的字符串,例:10px
  • 变量iCurrent使用parseInt(),将样式转成数字。
  1. element.style.leftelement.style[attr]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 运动框架-4-任意值变化
* @param {HTMLElement} element 运动对象
* @param {string} attr 需要改变的属性。
* @param {number} iTarget 目标值
*/
function startMove(element, attr, iTarget) {
clearInterval(element.timer);
element.timer = setInterval(function () {
//因为速度要动态改变,所以必须放在定时器中
var iCurrent=0;
iCurrent = parseInt(getStyle(element, attr));//实际样式大小
var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整
if (iCurrent === iTarget) {//结束运动
clearInterval(element.timer);
} else {
element.style[attr] = iCurrent + iSpeed + "px";
}
}, 30);
}

试一试,这样是不是就可以了呢?

还记得上面我们写的透明度变化吗? 再试试

果然还是不行, (废话,你见过透明度有”px”单位的么? - -白眼

第三步:透明度兼容处理

思考:需要对那些属性进行修改?

  1. 判断attr是不是透明度属性opacity
  2. 对于速度进行处理。
  • 为透明度时,由于获取到的透明度会是小数,所以需要 * 100
  • 并且由于计算机储存浮点数的问题,还需要将小数,进行四舍五入为整数。使用: Math.round(parseFloat(getStyle(element, attr)) * 100)
  • 否则,继续使用默认的速度。
  1. 对结果输出部分进行更改。
  • 判断是透明度属性,使用透明度方法
  • 否则,使用使用默认的输出格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 运动框架-5-兼容透明度
* @param {HTMLElement} element 运动对象
* @param {string} attr 需要改变的属性。
* @param {number} iTarget 目标值
*/
function startMove(element, attr, iTarget) {
clearInterval(element.timer);
element.timer = setInterval(function () {
//因为速度要动态改变,所以必须放在定时器中
var iCurrent = 0;
if (attr === "opacity") { //为透明度时执行。
iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100);
} else { //默认情况
iCurrent = parseInt(getStyle(element, attr)); //实际样式大小
}
var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整
if (iCurrent === iTarget) {//结束运动
clearInterval(element.timer);
} else {
if (attr === "opacity") { //为透明度时,执行
element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE
element.style.opacity = (iCurrent + iSpeed) / 100; //标准
} else { //默认
element.style[attr] = iCurrent + iSpeed + "px";
}
}
}, 30);
}

到这里,这个运动框架就基本上完成了。但是,我们是追求完美的不是吗?

继续进化!

(五)链式动画

链式动画:顾名思义,就是在该次运动停止时,开始下一次运动。

如何实现呢?

  • 使用回调函数:运动停止时,执行函数
  • 添加func形参(回调函数)。
  • 在当前属性到达目的地时iCurrent === iTarget,判断是否有回调函数存在,有则执行。
1
2
3
4
5
6
if (iCurrent === iTarget) {//结束运动
clearInterval(element.timer);
if (func) {
func();//回调函数
}
}

good,链式动画完成!距离完美还差一步!

(六)同时运动

思考:如何实现同时运动?

  1. 使用JSON传递多个值
  2. 使用for in循环,遍历属性,与值。
  3. 定时器问题!(运动提前停止)
  • 在循环外设置变量,假设所有的值都到达了目的值为true
  • 在循环中检测是否到达目标值,若没有值未到则为false
  • 在循环结束后,检测是否全部达到目标值.是则清除定时器

实现:

  1. 删除attriTarget两个形参,改为json
  2. 在函数开始时,设置一个标记var flag = true; //假设所有运动到达终点.
  3. 在定时器内使用for in,遍历属性与目标,改写原来的attriTarget,为json的属性与值
  4. 修改运动终止条件,只有每一项的实际属性值iCurrent,等于目标值json[attr]时,flag才为true。清除定时器,判断是否回调。
  5. 否则,继续执行代码,直到所有属性值等于目标值。

完美运动框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 获取实际样式函数
* @param {HTMLElement} element 需要寻找的样式的html节点
* @param {String]} attr 在对象中寻找的样式属性
* @returns {String} 获取到的属性
*/
function getStyle(element, attr) {
//IE写法
if (element.currentStyle) {
return element.currentStyle[attr];
//标准
} else {
return getComputedStyle(element, false)[attr];
}
}
/**
* 完美运动框架
* @param {HTMLElement} element 运动对象
* @param {JSON} json 属性:目标值
* @property {String} attr 属性值
* @config {Number} target 目标值
* @param {function} func 可选,回调函数,链式动画。
*/
function startMove(element, json, func) {
var flag = true; //假设所有运动到达终点.
clearInterval(element.timer);
element.timer = setInterval(function () {
for (var attr in json) {
//1.取当前的属性值。
var iCurrent = 0;
if (attr === "opacity") { //为透明度时执行。
iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100);
} else { //默认情况
iCurrent = parseInt(getStyle(element, attr)); //实际样式大小
}
//2.算运动速度,动画缓冲效果
var iSpeed = (json[attr] - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整

//3.未到达目标值时,执行代码
if (iCurrent != json[attr]) {
flag = false; //终止条件
if (attr === "opacity") { //为透明度时,执行
element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE
element.style.opacity = (iCurrent + iSpeed) / 100; //标准
} else { //默认
element.style[attr] = iCurrent + iSpeed + "px";
}
} else {
flag = true;
}
//4. 运动终止,是否回调
if (flag) {
clearInterval(element.timer);
if (func) {
func();
}
}
}
}, 30);
}

运动框架总结

  • 运动框架演变过程
框架 变化
startMove(element) 运动
startMove(element,iTarget) 匀速–>缓冲–>多物体
startMove(element,attr,iTargrt) 任意值
startMove(element,attr,iTargrt,func) 链式运动
startMove(element,json,func) 多值(同时)–>完美运动框架
使用搜索:谷歌必应百度