jquery及zepto的使用
# jQuery
jQuery是一个基于DOM操作的类库;
# 初始化
<title>检测是否正确引入Jquery</title>
<script type="text/javascript" src="public/js/jquery-2.2.3.min.js"></script>
<script>
// $(document).ready(function () {
// alert("jquery it work");
// });
$(function () {
alert("jquery it work");
});
</script>
2
3
4
5
6
7
8
9
10
# 选择器
# 基本选择器
ID 选择器和class 选择器
- 兼容性:ID兼容,class 不兼容IE6,7,8
- 数量: 通过ID只能获取
一个dom元素
,通过class可以获取一组元素
。 - 通用性:ID不能重复,class可以重复,所以class比较好用,这也是jQuery能被广泛应该的原因(选择器好)。
$('#LoginTextBox') // Returns element wrapped as jQuery object with id='LoginTextBox'
$('.active') // Returns all elements with CSS class active.
2
# 选择器的使用方式
- 并列:$("div,span, p.myClass")
- 家族:$("form input")
- 父子:$("form > input")
- 紧邻:$("label + input")
- 同辈[后辈]:$("form ~ input")
//选择所有在段落内部的超链接
$("p>a")
//p>a只能选中p标签里的子元素中的a标签,p a会选中p标签下所有(子孙元素)a标签
2
3
# 伪类
# 如何找到所有 HTML select 标签的选中项?
$('[name=NameOfSelectedTag] :selected').each(function(selected) {
alert($(selected).text());
});
$("#samy").click(function(){ //点击反选功能
$("[type=checkbox]").each(function(){ //type=checkbox实行便利循环
if(this.checked){} //判断type=checkbox里面是否有checked="checked"
})
})
2
3
4
5
6
7
8
9
js实现checkbox全选以及反选
<body>
<button id="other">反选</button>
<input type="checkbox" id="all" />全选
<input type="checkbox" class="check" />1
<input type="checkbox" class="check" />2
<input type="checkbox" class="check" />3
<script>
var checkbox = document.getElementsByClassName('check')
var checkAll = document.getElementById('all')
var checkOther = document.getElementById('other')
checkAll.onclick = function() {
var flag = true
for (var i = 0; i < checkbox.length; i++) {
if (!checkbox[i].checked) flag = false
}
if (flag) {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = false
}
} else {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = true
}
}
}
checkOther.onclick = function() {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = !checkbox[i].checked
}
}
</script>
</body>
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
# Sizzle选择器
# jQuery 通过哪个方法和 Sizzle 选择器结合
- Sizzle 选择器采取 Right To Left 的匹配模式,先搜寻所有匹配标签,再判断它的父节点
- jQuery 通过 $(selecter).find(selecter); 和 Sizzle 选择器结合;
# jQuery/css选择器的区别
- 两者的作用不同,CSS选择器找到元素后为设置该元素的样式,jQuery选择器找到元素后添加行为;
- jQuery选择器拥有更好的跨浏览器的兼容性;
# Dom元素/操作
# jQuery对象与Dom对象
# dom对象转换为jquery对象
一般情况下,dom对象直接用$()就可以转换成jquery对象,如:
$(document.getElementById("samy"))
# jquery对象转换成dom对象
一种是用jquery的内置函数get,来获取dom对象,如:
$("#samy").get(0)
还有一种方法更简单,因为jquery对象的属性是一个集合,所以我们可以像数组那样,取出其中一项就行:
$("#samy")[0];
$("div")[5];//上面这两种返回的都是dom对象,可以直接使用js里的方法
2
# document.getElementbyId("myId") 还是 $("#myId")更高效率?
第一种,因为它直接调用了 JavaScript 引擎。
document.getElementById("samy")😕/这种方法获取到的就是dom对象
$("#samy"): //这种方式获取得到的就是jquery对象
# html API
属性: attr(), removeAttr()
元素/节点内容: text(), html() and val(); 对于input是用val
文档处理:
- append appendTo: 将一个 HTML 元素添加到 DOM 树中
- **append():**在被选元素的结尾(但仍在元素内部)插入指定的内容。
$(selector).append(content);
- **appendto():**在被选元素的结尾(但仍在元素的内部)插入指定的内容。但不能使用函数来附加内容。
$(content).appendto(selector);
- **append():**在被选元素的结尾(但仍在元素内部)插入指定的内容。
- prepend prependTo
- after insertAfter
- before insertBefore
- wrap unwrap wrapAll()
- empty
- remove
- clone clone() and Cloning Event Handlers and Data
- replace target.replaceWith() and replaceAll(target)
- detach
- append appendTo: 将一个 HTML 元素添加到 DOM 树中
# detach() 和 remove() 方法的区别
尽管 detach() 和 remove() 方法都被用来移除一个DOM元素, 两者之间的主要不同:
在于
detach()
会保持对过去被解除元素的跟踪, 因此它可以被取消解除,而
remove()
方法则会保持过去被移除对象的引用.
//提取一个HTML 标记的属性 例如. 链接的href;
//例如. attr(name, value), 这里name是属性的名称,value是属性的新值。
$("#attr_set").click(function () {
alert($(this).attr('href'))
$("#img_src img").attr("src", "../../../../../../img/fengjing.jpg");
// <div data-id="something"></div>//注意这里有data-id属性;
// $("div").attr("data-id", "something");
/* $("div").attr({//设置属性回调
'data-id':'something',
class:'red'
});*/
});
2
3
4
5
6
7
8
9
10
11
12
# css API
class操作
- addClass
- removeClass
- toggleClass
- hasClass
添加和移除CSS类:通过利用 addClass() 和 removeClass() 这两个 jQuery 方法;
css 基础操作
- offset
- position
- scrollTop
- scrollLeft
- height
- width
- innerHeight
- innerWidth
- outerHeight
- outerWidth
$(document).ready(function () {
$('button').on("click",function () {
//$('li:first').css('backgroundColor','yellow');
$('li:first').css({//一起设置多个
'backgroundColor':'yellow',
'font-size':'40px'
});
$('li:first').css('font-size',function (index, oldValue) {//设置时回调
alert(oldValue);//40px
return '20px';//最后为20px;
});
});
});
2
3
4
5
6
7
8
9
10
11
12
13
# html/css完整案例
$(function () {
// 属性 操作 获取,设置
$("#attr_get").click(function () {
alert($("#img_src img").attr("src"));
});
$("#attr_set").click(function () {
$("#img_src img").attr("src", "../../../../../../img/fengjing.jpg");
// <div data-id="something"></div>//注意这里有data-id属性;
// $("div").attr("data-id", "something");
/* $("div").attr({//设置属性回调
'data-id':'something',
class:'red'
});*/
});
$("#attr_remove").click(function () {
$("#img_src img").removeAttr("src","../../../../../../img/fengjing.jpg");
// $("div").removeAttr("data-id");
});
// 节点数值的获取 html text val
// 获取
$("#node_html").click(function () {
// alert($("#get_html").html());//获取
// $("#get_html").html("<p>add html</p><p>by click</p>");//替换
$("#get_html").html(function (index, html) {//有回调的设置
alert(index+"===:==="+ html);
});
});
$("#node_text").click(function () {
alert($("#get_text").text());
});
$("#node_val").click(function () {
alert($("#get_val").val());
});
// 设置
$("#node_shtml").click(function () {
$("#get_html").html('<strong>man</strong>');
});
$("#node_stext").click(function () {
$("#get_text").text('man');
});
$("#node_sval").click(function () {
//对于input是用val
$("#get_val").val('不约!');
});
// 文档处理 append prepend
$("#dom_add").click(function () {
$("#fabao ul").append('<li>老师,有第四条吗?</li>')
});
$("#dom_pre_add").click(function () {
$("#fabao ul").prepend('<li>老师,我弱弱的问一下?</li>')
});
// 文档处理 appendTo prependTo
$("#dom_to_add").click(function () {
$('<li>老师,有第四条吗?</li>').appendTo("#fabao ul")
});
$("#dom_preto_add").click(function () {
$('<li>老师,我弱弱的问一下?</li>').prependTo("#fabao ul")
});
// 文档处理 after before
$("#dom_after").click(function () {
$("#gongzi").after('<li>这个可以有!</li>');
});
$("#dom_before").click(function () {
$("#gongzi").before('<li>嘿嘿!</li>');
});
// 文档处理 insertAfter insertBefore
$("#dom_insert_after").click(function () {
$('<li>这个可以有!</li>').insertAfter("#gongzi");
});
$("#dom_insert_before").click(function () {
$('<li>嘿嘿!</li>').insertBefore("#gongzi");
});
// 文档处理 wrap unwrap
$("#dom_wrap").click(function () {
$("#baoguo p").wrap('<div style="color:#a52a2a"></div>');
});
$("#dom_unwrap").click(function () {
$("#baoguo p").unwrap('<div style="color:#a52a2a"></div>');
});
// 文档处理 empty remove clone
$("#dom_empty").click(function () {
$("#node_empty").empty();//只移除文字信息;
});
$("#dom_remove").click(function () {
$("#node_remove").remove();//只移除控件
});
$("#dom_clone").click(function () {
alert($("#node_clone").clone().html());
$("#node_clone").before($("#node_clone").clone())
});
$("#dom_replace").click(function () {
// $("#node_replace").replaceWith("<p>node_replace by samy</p>");
$('<p>node_replace by samy</p>').replaceAll($("#node_replace"));
});
// CSS 类 操作
$("#dom_addClass").click(function () {
$("#dom_class").addClass('new_style');
});
$("#dom_removeClass").click(function () {
$("#dom_class").removeClass('new_style');
});
$("#dom_toggleClass").click(function () {
$("#dom_class").toggleClass('new_style');
alert($("#dom_class").hasClass('new_style'));
alert(JSON.stringify($("#dom_class").css(['color','background-color'])));
});
// CSS 基础操作//详见之前【选择器】操作
$("#dom_css").click(function () {
$("#dom_css_set").css('color', 'red');
$("#dom_css_set").css({border: "red solid 1px"});
});
$("#dom_height").click(function () {
$("#dom_css_set").height('300px');
});
})
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# 事件
# 浏览器事件
- ready 文档就绪事件(当 HTML 文档就绪可用时)
加载/退出 ready() : document.onload
提示:ready() 函数不应与
<body onload="">
一起使用。- ready() 函数用于在文档进入ready状态时执行代码。
- 当DOM 完全加载(例如HTML被完全解析DOM树构建完成时),jQuery允许你执行代码。使用**$(document).ready()的最大好处在于它适用于所有浏览器,jQuery帮你解决了跨浏览器的难题**。
- resize 触发、或将函数绑定到指定元素的 resize 事件 尺寸缩放
- scroll 触发、或将函数绑定到指定元素的 scroll 事件
# window.onload 事件和 jQuery ready 函数区别
1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行。 $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕。
2.编写个数不同 window.onload不能同时编写多个,如果有多个window.onload方法,只会执行一个 $(document).ready()可以同时编写多个,并且都可以得到执行
3.简化写法 window.onload没有简化写法 $(document).ready(function(){})可以简写成$(function(){});
# 事件处理方式
- on
- bind 向匹配元素附加一个或更多事件处理器
- unbind 从匹配元素移除一个被添加的事件处理器
- trigger 所有匹配元素的指定事件
# 在一个 jQuery 事件处理程序里返回了 false
这通常用于阻止事件向上冒泡。
# bind(),live(),delegate(),on()的区别
jquery中bind(),live(),delegate()都是基于on实现的,on是封装了一个兼容的事件绑定方法,在选择元素上绑定一个或多个事件的事件处理函数;
- bind(type,[data],fn) 为每个匹配元素的特定事件绑定事件处理函数
- live(type,[data],fn) 给所有匹配的元素附加一个事件处理函数,即使这个元素是以后再添加进来的
- delegate(selector,[type],[data],fn) 指定的元素(属于被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数
差别:
- .bind()是直接绑定在元素上
- .live()则是通过冒泡的方式来绑定到元素上的。更适合列表类型的,绑定到document DOM节点上。和.bind()的优势是支持动态数据。
- .delegate()则是更精确的小范围使用事件代理,性能优于.live()
- .on()则是最新的1.9版本整合了之前的三种方式的新事件绑定机制
# 自定义事件及fire函数
事件即“发布/订阅”模式,自定义事件即“消息发布”,事件的监听即“订阅订阅” ; jQuery中有更简单的trigger()方法实现自定义事件功能
jQuery 里的 fire 函数用于调用 jQuery 自定义事件列表中的事件
$("#btn").bind("myEventName",function(){//绑定到自定义事件和自定义事件触发时将被执行
alert("myEventName triggered");
});
$event.trigger("myEventName");
$('input').trigger('focus')//trigger()方法触发事件后,会执行浏览器默认操作
2
3
4
5
6
传递参数:
<button id="btn">按钮</button>
<p id="msg"></p>
<script>
$(function(){
$('#btn').bind("clickMe",function(event,msg1,msg2){
$("#msg").text(msg1+' '+msg2)
})
$('#btn').trigger("clickMe",["hello","jquery"])
})
</script>
2
3
4
5
6
7
8
9
10
one方法
one方法的功能是让所选定的元素绑定一个仅触发一次的处理函数,格式为:one(type,${data},fn)
<script>
$(function() {
function btn_Click() {
this.value = "abc123"
}
$("input").one("click", btn_Click); //绑定自定义事件
})
</script>
</head>
<body>
<input id="Button1" type="button" value="点击查看联系方式" class="btn" />
2
3
4
5
6
7
8
9
10
11
JS 原生支持自定义事件步骤:
- 创建事件
document.createEvent(type)
- 自定义事件的类型有(即document.createEvent('HTMLEvents')中的参数):
- UIEvents:一般化的UI事件。
- MouseEvents:一般化的鼠标事件。
- MutationEvents:一般化的DOM变动事件。
- HTMLEvents:一般化的HTML事件。
- 自定义事件的类型有(即document.createEvent('HTMLEvents')中的参数):
- 初始化事件
event.initEvent(eventType, canBubble, prevent)
- 监听事件
target.addEventListener('dataavailable', handler, false)
- 触发事件
target.dispatchEvent(e)
var event = document.createEvent('HTMLEvents');
event.initEvent('click', true, true);//接受3个参数: 事件类型,是否冒泡,是否阻止浏览器的默认行为
event.eventType = 'click';
a.addEventListener('click', function(){
console.log(input.value);
console.log(this.getAttribute('href'));
console.log(location.href);
}, false); //a是我已经通过id获得好的一个a标签
a.dispatchEvent(event); //触发a上绑定的自定义事件
//注:jQuery中有更简单的trigger()方法实现自定义事件功能
2
3
4
5
6
7
8
9
10
11
自定义事件的发生比较容易控制,你什么时候触发(dispatchEvent/fireEvent)它,它就什么时候发生。
# 同时绑定多个事件
# 一个对象可以同时绑定多个事件,这是如何实现的?
jquery中事件绑定的函数中传递多个事件参数,执行事件的时候判断执行事件的类型
//多个事件同一个函数:
$("div").on("click", "mouseover", function(){});
//多个事件不同函数
$("div").on({
click: function(){},
mouseover: function(){}
});
2
3
4
5
6
7
# 父元素div绑定两个事件(一个冒泡阶段、一个捕获阶段),子元素也是这种情况。事件触发顺序如何。
var div = document.querySelector('div');
var btn = document.querySelector('button');
//useCapture 可选。布尔值,指定事件是否在捕获或冒泡阶段执行。
// true - 事件句柄在捕获阶段执行
//false- false- 默认。事件句柄在冒泡阶段执行
div.addEventListener('click', function(){
console.log('bubble','div');//1
},false);
div.addEventListener('click', function(){
console.log('capture','div');//4
},true);
btn.addEventListener('click', function(){
console.log('bubble','btn');//2
},false);
btn.addEventListener('click', function(){
console.log('capture','btn');//3
},true);
//capture div
//bubble btn
//capture btn
//bubble div
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
捕获事件,后发生冒泡事件。所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。
<script>
$(function () {
// 点击
$("#click1").click(function () {
alert("你点击我了");
});
// 悬停
$("#hover1").hover(function () {
alert("悬停进入事件");
}, function () {
alert("悬停离开事件");
});
// focus blur 事件
$("#input1").blur(function () {
alert("blur事件");
});
$("#input1").focus(function () {
$(this).blur();// 否则会不断的 执行这个,
alert("focus事件");
});
// 键盘事件
$("#input2").keydown(function () {
$("#target1").text($(this).val())
});
$("#input3").keyup(function () {
$("#target1").text($(this).val())
});
$("#input4").keypress(function () {
$("#target1").text($(this).val())
});
// 事件验证 on bind trigger live
$("#t_on").on("click", function () {
alert("on 形式的点击");
});
$("#t_bind").bind("click", function () {
alert("bind 形式的点击");
});
$("#t_trigger").click(function () {
alert("这个动作会触发 #t_on 的点击事件");
$("#t_on").trigger("click");
});
$("#t_live").click(function () { n
$("#jiazai").append('<span id="gaibian">装载完成....请点击测试.</span>');
$("#gaibian").click(function () {
alert("live/ 补充方式 形式的点击");
});
});
$('#ip2').on('keypress', function(){
$('#result3').append('<br/>keypress');
});
$('#ip3').on('change', function(){
$('#result4').html('changed');
});
$('#form1').on('submit', function(){
alert('submit!');
});
})
</script>
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
# 动画
# 基本动画
# 方法
- show( speed, [callback])
- hide( speed, [callback])
- toggle( speed, [callback])
描述 $(selector).方法(speed,callback); 可选的 speed 参数规定隐藏/显示的速度,可以取以下值:"slow"、"fast" 或毫秒。 可选的 callback 参数是隐藏或显示完成后所执行的函数名称。
toggle默认切换hide()和show() 如果你在toggle()方法自定义多个方法,则toggle()是切换你的方法,toggle语法实际如下: $(selector).toggle(function1(),function2(),functionN(),...)
$("button").click(function(){
$("p").hide(1000);
});
$('#ButtonToClick').click(function(){
$('#ImageToHide').hide();//.toggle();
});
2
3
4
5
6
# 动画优化
# jQuery 的 slideUp动画 ,如果目标元素是被外部事件驱动, 当鼠标快速地连续触发外部元素事件, 动画会滞后的反复执行,该如何处理呢?
- 在触发元素上的事件设置为延迟处理:使用 JS 原生 setTimeout 方法
- 在触发元素的事件时预先停止所有的动画,再执行相应的动画事件:$('.tab').stop().slideUp();
// 上下滑动【卷帘门效果】
// 显示
$("#t_slideDown").click(function () {
$('.img_container').slideDown();
});
// 隐藏
$("#t_slideUp").click(function () {
$('.img_container').slideUp();
});
// toggle
$("#t_slideToggle").click(function () {
$('.img_container').slideToggle();
});
2
3
4
5
6
7
8
9
10
11
12
13
# 正则验证
详见【js中的详解】
# 验证插件的使用
jQuery Validate https://jqueryvalidation.org/
# jQuery要点其他
# 实现原理
(function( window, undefined ) {//A
//用一个函数域包起来,就是所谓的沙箱
//在这里边var定义的变量,属于这个函数域内的局部变量,避免污染全局
//把当前沙箱需要的外部变量通过函数参数引入进来
//只要保证参数对内提供的接口的一致性,你还可以随意替换传进来的这个参数
window.jQuery = window.$ = jQuery;//B
})( window );
2
3
4
5
6
7
(function(window, undefined) {})(window);
:jQuery 利用 JS 函数作用域的特性,采用立即调用表达式包裹了自身,解决命名空间和变量污染问题window.jQuery = window.$ = jQuery;
在闭包当中将 jQuery 和 $ 绑定到 window 上,从而将 jQuery 和 $ 暴露为全局变量
jQuery或zepto源码写的好的地方
- jquery源码封装在一个匿名函数的自执行环境中,有助于防止变量的全局污染;
- 通过传入window对象参数,可以使window对象作为局部变量使用,好处是当jquery中访问window对象的时候,就不用将作用域链退回到顶层作用域了**,从而可以更快的访问window对象**。
- 同样,传入undefined参数,可以缩短查找undefined时的作用域链
- jquery将一些原型属性和方法封装在了jquery.prototype中,为了缩短名称,又赋值给了>jquery.fn,这是很形象的写法;
- jquery实现的链式调用可以节约代码,所返回的都是同一个对象,可以提高代码效率。 jquery的优势就是链式操作,隐式迭代;
# 入口函数
jQuery 入口函数:
//两种方式: 实现文档就绪后执行 jQuery 方法。
$(document).ready(function(){
// 开始写 jQuery 代码...
});
//简洁写法(与以上写法效果相同):
$(function(){
// 开始写 jQuery 代码...
});
2
3
4
5
6
7
8
JavaScript 入口函数:
window.onload = function () {
// 执行代码
}
2
3
jQuery 入口函数
与 JavaScript 入口函数
的区别
- jQuery 的入口函数是在 html 所有标签(DOM)都加载之后,就会去执行。执行次数:可以执行多次,第 N 次都不会被上一次覆盖;
DOMContentLoaded
- JavaScript 的 window.onload 事件是等到所有内容,包括外部图片之类的文件加载完后,才会执行。执行次数:只能执行一次,如果第二次,那么第一次的执行会被覆盖;
# $() 函数
$() 函数是 jQuery() 函数的别称; $() 函数用于将任何对象包裹成 jQuery 对象,接着你就被允许调用定义在 jQuery 对象上的多个不同方法。可以将一个选择器字符串传入 $() 函数,它会返回一个包含所有匹配的 DOM 元素数组的 jQuery 对象。
$ = function(){...}();
/func = function(){...};
$ = func();
$(".div1") //表示获取类名为div1的元素,例如获取<div class="div1"></div>
$(".div1").onclick //表示类名为div1的div点击事件
//jquery中$.,例如$.post(),$.get(),$.ajax()等这些都是jquery这个对象的方法
2
3
4
5
6
jQuery 名称冲突 jQuery 使用 $ 符号作为 jQuery 的简介方式。 某些其他 JavaScript 库中的函数(比如 Prototype)同样使用 $ 符号。 jQuery 使用名为 noConflict() 的方法来解决该问题。 var jq=jQuery.noConflict(),帮助您使用自己的名称(比如 jq)来代替 $ 符号。
# extend/ fn.extend
# 简介
- $.extend(object); // 为jQuery添加“静态方法”(工具方法);
- $.extend([true,] targetObject, object1[, object2]); // 对targt对象进行扩展;
- $.fn.extend(json); // 为jQuery添加“成员函数”(实例方法); 源码中jquery.fn = jquery.prototype,所以对jquery.fn的扩展,就是为jquery类添加成员函数
$.extend({
min: function(a, b) { return a < b ? a : b; },
max: function(a, b) { return a > b ? a : b; }
});
$.min(2,3); // 2
$.max(4,5); // 5
var settings = {validate:false, limit:5};
var options = {validate:true, name:"bar"};
$.extend(settings, options); // 注意:不支持第一个参数传 false
// settings == {validate:true, limit:5, name:"bar"}
$.fn.extend({
alertValue: function() {
$(this).click(function(){
alert($(this).val());
});
}
});
$("#email").alertValue();
//比如我们要开发一个插件,做一个特殊的编辑框,当它被点击时,便alert 当前编辑框里的内容。
$.fn.extend({
alertWhileClick:function() {
$(this).click(function(){
alert($(this).val());
});
}
});
//页面上为$("#input1")为一个jQuery实例,便实现了扩展,每次被点击时它会先弹出目前编辑里的内容。
$("#input1").alertWhileClick();
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
# fn函数
jQuery.fn = jQuery.prototype 即指向jQuery对象的原型链,对其它进行的扩展,作用在jQuery对象上面;一般用此方法来扩展jQuery的对象插件
jQuery.fn的init方法返回的this指的就是当前操作后的jquery对象,为了实现jquery的链式操作
源码:.fn是指jQuery的命名空间,fn上的成员(方法function及属性property),会对jQuery实例每一个有效。
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {//....
};
2
3
- jQuery.fn 的 init 方法 返回的 this 就是 jQuery 对象
- 用户使用 jQuery() 或 $() 即可初始化 jQuery 对象,不需要动态的去调用 init 方法
# 拷贝(extend)的实现原理
浅拷贝(只复制一份原始对象的引用)
var newObject = $.extend({}, oldObject);
深拷贝(对原始对象属性所引用的对象进行进行递归拷贝); 这种方式会完全拷贝所有数据,优点是与不会相互依赖(完全脱离关联),缺点是拷贝的速度更慢,代价更大。
var newObject = $.extend(true, {}, oldObject);
jQuery.extend( target [, object1 ] [, objectN ] ):合并object1, objectN到target对象,如果只有一个参数,则该target对象会被合并到jQuery对象中
jQuery.extend( [deep ], target, object1 [, objectN ] ):深度复制合并对象,第一个参数是boolean类型的true时,将object1, objectN深度复制后合并到target中;关于深度复制,是将除null, undefined,window对象,dom对象,通过继承创建的对象外的其它对象克隆后保存到target中;
深度与非深度复制区别是,深度复制的对象中如果有复杂属性值(如数组、函数、json对象等),那将会递归属性值的复制,合并后的对象修改属性值不影响原对象
obj1 = { a : 'a', b : 'b' };
obj2 = { x : { xxx : 'xxx', yyy : 'yyy' }, y : 'y' };
$.extend(true, obj1, obj2);
alert(obj1.x.xxx); // 得到"xxx"
obj2.x.xxx = 'zzz'; //修改obj2对象属性的内联值,不影响合并后对象obj1
alert(obj2.x.xxx); // 得到"zzz"
alert(obj1.x.xxx); // 得到"xxx" //值保持;如果不加true,则得到“zzz”
2
3
4
5
6
7
# extend 与 fn.extend的区别
jQuery.extend 扩展jQuery全局, 静态方法; 为jQuery类添加类方法; 可以理解为添加静态方法 jQuery.fn.extend(object); 扩展jQuery对象方法
jQuery.extend 为jquery类添加类方法,可以理解为添加静态方法;
jQuery.extend(object); 为扩展jQuery类本身,为自身添加新的方法。 jQuery.fn.extend(object);给jQuery对象添加方法。 源码中jquery.fn = jquery.prototype,所以对jquery.fn的扩展,就是为jquery类添加成员函数
jQuery.extend({
sayhello:function(){
console.log("Hello,This is jQuery Library");
}
})
$.sayhello(); //Hello, This is jQuery Library
jQuery.fn.extend({
check: function() {
return this.each(function() {
this.checked = true;
});
},
uncheck: function() {
return this.each(function() {
this.checked = false;
});
}
})
$( "input[type='checkbox']" ).check(); //所有的checkbox都会被选择
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 扩展序列化及反序列化功能
//jQuery 中没有提供这个功能,所以需要先编写两个jQuery的扩展: 简单扩展;内部实现,详见下面;
$.fn.stringifyArray = function(array) {
return JSON.stringify(array)
}
$.fn.parseArray = function(array) {
return JSON.parse(array)
}
//然后调用:
$("#xxx").stringifyArray(array)//$("#xxx")为一个jQuery实例
// 通过原生 JSON.stringify/JSON.parse 扩展 jQuery 实现
$.array2json = function(array) {
return JSON.stringify(array);
}
$.json2array = function(array) {// $.parseJSON(array); // 3.0 开始,已过时
return JSON.parse(array);
}
// 调用
var json = $.array2json(['a', 'b', 'c']);
var array = $.json2array(json);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
新版本
已经实现: $.parseJSON() 函数用于将符合标准格式的的JSON字符串转为与之对应的JavaScript对象。
$(function () {
var obj = jQuery.parseJSON('{"name":"samy"}');
alert( obj.name === "samy" );
})
var str = '[{"href":"baidu.com","text":"test","orgId":123,"dataType":"curry"}]';
jQuery.parseJSON(str);
2
3
4
5
6
7
JSON.parse()和jQuery.parseJSON()的区别
有的浏览器不支持JSON.parse()方法,使用jQuery.parseJSON()方法时,在浏览器支持时会返回执行JSON.parse()方法的结果,否则会返回类似执行eval()方法的结果;
parseJSON: function( data ) {
// Attempt to parse using the native JSON parser first
if ( window.JSON && window.JSON.parse ) {
return window.JSON.parse( data );
}
if ( data === null ) {
return data;
}
if ( typeof data === "string" ) {
// Make sure leading/trailing whitespace is removed (IE can't handle it)
data = jQuery.trim( data );
if ( data ) {
// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if ( rvalidchars.test( data.replace( rvalidescape, "@" )
.replace( rvalidtokens, "]" )
.replace( rvalidbraces, "")) ) {
return ( new Function( "return " + data ) )();
}
}
}
jQuery.error( "Invalid JSON: " + data );
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 深浅拷贝的实现
$ = { //浅复制的模拟实现
extend : function(target, options) { //浅拷贝
for (name in options) {
target[name] = options[name];
}
return target;
},
extend : function(deep, target, options) { //深拷贝
for (name in options) {
copy = options[name];
if (deep && copy instanceof Array) {
target[name] = $.extend(deep, [], copy);
} else if (deep && copy instanceof Object) {
target[name] = $.extend(deep, {}, copy);
} else {
target[name] = options[name];
}
}
return target;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
深拷贝分析: 跟js中对象的深拷贝类似;
具体分为三种情况:
1. 属性是数组时,则将target[name]初始化为空数组,然后递归调用extend; 2. 属性是对象时,则将target[name]初始化为空对象,然后递归调用extend; 3. 否则,直接复制属性。
源码实现:
$ = function() {
var copyIsArray,
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty;
class2type = {
'[object Boolean]' : 'boolean',
'[object Number]' : 'number',
'[object String]' : 'string',
'[object Function]' : 'function',
'[object Array]' : 'array',
'[object Date]' : 'date',
'[object RegExp]' : 'regExp',
'[object Object]' : 'object'
},
type = function(obj) {
return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
},
isWindow = function(obj) {
return obj && typeof obj === "object" && "setInterval" in obj;
},
isArray = Array.isArray || function(obj) {
return type(obj) === "array";
},
isPlainObject = function(obj) {
if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
return false;
}
if (obj.constructor && !hasOwn.call(obj, "constructor")
&& !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
var key;
for (key in obj) {
}
return key === undefined || hasOwn.call(obj, key);
},
extend = function(deep, target, options) {
for (name in options) {
src = target[name];
copy = options[name];
if (target === copy) { continue; }
if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && isArray(src) ? src : [];
} else {
clone = src && isPlainObject(src) ? src : {};
}
target[name] = extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
return target;
};
return { extend : extend };
}();
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
# jQuery 的队列
定义及使用场景
- jQuery 核心中有一组队列控制方法,由 queue()/dequeue()/clearQueue() 三个方法组成。
- 主要应用于 animate(),ajax,其他要按时间顺序执行的事件中
var func1 = function(){alert('事件1');}
var func2 = function(){alert('事件2');}
var func3 = function(){alert('事件3');}
var func4 = function(){alert('事件4');}
// 入栈队列事件
$('#box').queue("queue1", func1); // push func1 to queue1
$('#box').queue("queue1", func2); // push func2 to queue1
// 替换队列事件
$('#box').queue("queue1", []); // delete queue1 with empty array
$('#box').queue("queue1", [func3, func4]); // replace queue1
// 获取队列事件(返回一个函数数组)
$('#box').queue("queue1"); // [func3(), func4()]
// 出栈队列事件并执行
$('#box').dequeue("queue1"); // return func3 and do func3
$('#box').dequeue("queue1"); // return func4 and do func4
// 清空整个队列
$('#box').clearQueue("queue1"); // delete queue1 with clearQueue
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
动画中用到的示例:
var _slideFun = [
function() {
$('.one').delay(500).animate({
top: '+=270px'
},500, _takeOne);
},
function() {
$('.two').delay(300).animate({
top: '+=270px'
},500, _takeOne);
}
];
$('#demo').queue('slideList', _slideFun);
var _takeOne = function() {
$('#demo').dequeue('slideList');
};
_takeOne();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 性能的优化方法【要点】
- 缓存频繁操作DOM对象; 频繁操作的DOM,先缓存起来再操作。
- 尽量使用id选择器代替class选择器; 因为需遍历所有DOM元素。
- 总是从#id选择器来继承;
- 尽量使用链式操作; 用Jquery的链式调用更好。
- 使用事件委托 on 绑定事件;
- 采用jQuery的内部函数data()来存储数据;
- 使用最新版本的 jQuery;
示例:
var str=$("a").attr("href");//比如:
for (var i = size; i < arr.length; i++) {}
for (var i = size, length = arr.length; i < length; i++) {}// 优化后
2
3
# 相关问题
# 为什么要将JS源文件的全部内容包装在一个函数中?
这是一种越来越普遍的做法,被许多流行的JS库所采用。 这种技术围绕文件的整个内容创建一个闭包,最重要的是,它可以创建一个私有命名空间,从而有助于避免不同JS模块和库之间潜在的名称冲突。
该技术的另一个特性是允许为全局变量提供一个简单的别名,这在jQuery插件中经常使用。
# $(this) 和 this 关键字在 jQuery 中有何不同
- this表示的是javascript提供的当前对象
- $(this)表示的是用jquery封装候的当前对象
# jQuery 中的方法链是什么?使用方法链有什么好处?
方法链是对一个方法返回的结果调用另一个方法,这使得代码简洁明了,同时由于只对 DOM 进行了一轮查找,性能方面更加出色。
# jQuery UI
# jQuery 与 jQuery UI、jQuery Mobile 区别
- jQuery 是 JS 库,兼容各种PC浏览器,主要用作更方便地处理 DOM、事件、动画、AJAX
- jQuery UI 是建立在 jQuery 库上的一组用户界面交互、特效、小部件及主题
- jQuery Mobile 以 jQuery 为基础,用于创建“移动Web应用”的框架
# jQuery UI自定义组件
通过向 $.widget() 传递组件名称和一个原型对象来完成;
示例:$.widget("ns.widgetName", [baseWidget], widgetPrototype);
# zepto
# 引入
方式一:在页面中引入并使用Zepto.js; Bower方式安装; 使用zepto.min.js
npm install -g bower
bower install zepto
tree
2
3
方式二:Zepto.js自定义构建方法; http://github.e-sites.nl/zeptobuilder/
方式三:处理在线Hack方式
var ndParent = document.getElementsByTagName("script")[0];
var ndScript = document.createElement("script");
//ndScript.src = "http://zeptojs.com/zepto.js";
ndScript.src = "http://127.0.0.1:8080/dist/zepto.custome-touch.js";
ndScript.onload = function () {
if (window.Zepto){
console.log("injected");
window.$ = window.Zepto;
}
};
ndParent.parentNode.appendChild(ndScript);
2
3
4
5
6
7
8
9
10
11
![](~/20-37-51.jpg)
# 常用方法
# $
用法
- $(selector, [context]) ⇒ collection
-
$(<Zepto collection>) ⇒ same collection
-
$(<DOM nodes>) ⇒ collection
- $(htmlString) ⇒ collection
- $(htmlString, attributes) ⇒ collection v1.0+
- Zepto(function($){ ... })
说明
- 使用$选择元素
- 使用$构造DOM
- 使用$绑定页面事件
- $全局变量的注意事项 :$==Zepto
$('div') //=> all DIV elements on the page
$('#foo') //=> element with ID "foo"
// create element:
$("<p>Hello</p>") //=> the new P element
// create element with attributes:
$("<p />", { text:"Hello", id:"greeting", css:{color:'darkblue'} })
//=> <p id=greeting style="color:darkblue">Hello</p>
// execute callback when the page is ready:
Zepto(function($){
alert('Ready to Zepto!')
})
2
3
4
5
6
7
8
9
10
11
12
13
# 数组
$.each, $.map, $.grep => Array.prototype 遍历对象
# 对象
$.extend => Object.assign
# 工具
$.is* => typeof, instanceof
# 节点
$.length $.camelCase $.trim
# 字符
$.trim, $parseJSON
常用方法示例:
/* $(function () {
console.log("zepto page.dome ready");
});*/
//遍历对象
console.log("遍历对象:");
$.each({a: "name"},function (key, value){
console.log({key:key, value:value});//打印的是对象;Object {key: "a", value: "name"}
});
//遍历数组
console.log("遍历数组:");
$.each([1,2,3,"a"],function (index, value){
//[0, 1] [1, 2] [2, 3] [3, "a"]
console.log(arguments);
});
//遍历数组
console.log("遍历数组:forEach");
var arr = [1,"b",3];
arr.forEach(function (key, value){
console.log({key:key, value:value});//Object {key: 1, value: 0} Object {key: b, value: 1} Object {key: 3, value: 2}
});
console.log("遍历数组:each");
$.each(arr,function (key, value){
console.log({key:key, value:value});//Object {key: 0, value: 1} Object {key: 1, value: b} Object {key: 2, value: 3}
});
console.log("遍历Map");
var arr = [1,"b",3];
var map = $.map(arr,function(value){
return value * value;
});
console.log(map);//[1, NaN, 9]
console.log("Grep用法:");
var arr = [1,"b",3];
var grep = $.grep(arr,function(value){//[1, NaN, 9]
return value %3==0;
});
console.log(grep);//[3]
console.log("Extend用法:");
var obj1 = {name: "samy"};
var obj2 = {gender: "男"};
console.log($.extend(obj1,obj2));//Object {name: "samy", gender: "男"}
var obj1 = {name: "samy"};
var obj2 = {name: "zhang",gender: "男"};//原来key的字段会被冲突掉;
console.log($.extend(obj1,obj2));//Object {name: "zhang", gender: "男"}
console.log("is*用法:");
var obj1 = {name: "samy"};
console.log($.isPlainObject(obj1));//true
console.log($.isArray([]));//true
console.log("CamelCase用法:");
var str = "Samy zhang";
console.log($.camelCase(str));//Samy zhang
console.log($.camelCase("hello-there"));//helloThere
console.log("length trim用法:");
var str = "Samy zhang ";
console.log(str.length,$.trim(str).length);//12 10
// console.log("contains用法:");
/* var str = "Samy zhang ";
console.log(str.contains("Samy"));//12 10*/
console.log("parseJSON用法:");//把Json转化成对象;
// var obj1 = {name: "samy"};
var obj1 = '{"name": "samy"}';
console.log($.parseJSON(obj1));//Object {name: "samy"}
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
62
63
64
65
66
67
68
69
70
71
# 用Zepto增删改查节点
# 插入节点
- insetAfter
- insetBefore
- append
- prepend
减少节点
- remove
修改节点
- html
- css
- attr
- props
- *class
获取节点
- parent
- prev
- next
- find
- children
计算节点
- width
- height
- position
- offset
- data
# Detect
检测浏览器允许环境; 构建包含Detect模块的Zepto版本;$.os、$browser用法
# Event
# 高效管理
- 事件绑定、解绑:on 、off
- 事件委托机制:on
- 自定义事件机制:trigger
# 移动端事件(touch)
构建包含Zepto移动端事件的版本 通过命令行构建 通过WEB界面构建 使用移动端事件 手势事件模块
# 方法
$.Event
$.proxy
bind
delegate
die
event.isDefaultPrevented
event.isImmediatePropagationStopped
event.isPropagationStopped
live
off
on
one
trigger
triggerHandler
unbind
undelegate
# Ajax
# 异步加载数据
- $.ajax
- $.get、$.post、 load
- $.param
# jQuery.get() 和 jQuery.ajax() 方法之间的区别
ajax() 方法更强大,更具可配置性, 让你可以指定等待多久
,以及如何处理错误。get() 方法是一个只获取一些数据的专门化方法。
# 方法
- $.ajax
- $.ajaxJSONP
- $.ajaxSettings
- $.get
- $.getJSON
- $.param
- $.post
- load
# 使用单页应用将文件上传到服务器的有哪些方法?
XMLHttpRequest2(streaming),fetch(non-streaming),File API
# Form
# 操作表单
表单的数据序列化; 表单的提交事件
# 方法
serialize serializeArray submit
# Effects
方法: $.fx animate
# jQuery 和 Zepto 的区别及使用场景
- jQuery 主要目标是PC的网页中,兼容全部主流浏览器。在移动设备方面,单独推出 jQuery Mobile
- Zepto 从一开始就定位移动设备,相对更轻量级。它的 API 基本兼容 jQuery,但对PC浏览器兼容不理想
# 使用上的区别
# 事件
# tap事件点透问题
场景
当A/B两个层上下z轴重叠,上层的A点击后消失或移开(这一点很重要),并且B元素本身有默认click事件(如a标签)或绑定了click事件。在这种情况下,点击A/B重叠的部分,就会出现点透的现象。
原因
zepto的tap事件是通过监听绑定在document上的touch事件来完成tap事件的模拟的,并且tap事件是冒泡到document上触发的;在移动端不使用click而用touch事件代替触摸是因为click事件有着明显的延迟,具体touchstart与click的区别如下:
touchstart:在这个DOM(或冒泡到这个DOM)上手指触摸开始即能立即触发;
click:在这个DOM(或冒泡到这个DOM)上手指触摸开始,且手指未曾在屏幕上移动,且在这个DOM上手指离开屏幕,且触摸和离开屏幕之间的间隔时间较短(某些浏览器不检测间隔时间,也会触发click)才能触发
也就是说,在移动端事件的触发时间按由早到晚排列为:touchstart 早于 touchend 早于 click。亦即click的触发是有延迟的,这个时间大概在300ms左右。
由于我们在touchstart阶段就已经隐藏了罩层A,当click被触发时候,能够被点击的元素则是其下的B元素,根据click事件的触发规则:**只有在被触发时,当前有click事件的元素显示,且在面朝用户的最前端时,才触发click事件。**由于B绑定了click事件(或者B本身默认存在click事件),所以B的click事件被触发,产生了点透的情况。
解决方案
方案一:来得很直接github上有个fastclick可以完美解决
# 为什么基本相同的代码,zepto会点透而fastclick不会呢?
**原因: **zepto的代码里面有个settimeout,在settimeout里面执行e.preventDefault()不会生效,因此zepto中的延迟300ms的click事件会触发,而fastClick不会。
所以zepto的tap事件(通过touchstart和touchend模拟出来的)有点透问题,而fastClick的click事件(通过touchstart和touchend模拟出来的)没有。
因为zepto的tap事件统一是在document的touchend时触发的,若在这里使用e.preventDefault(),那页面上所有元素在touchend后触发的事件都不会被执行了。fastClick使用了touch事件但是touch事件是绑定到了具体dom而不是document上;
//引入fastclick.js,因为fastclick源码不依赖其他库所以你可以在原生的js前直接加上
window.addEventListener( "load", function() {
FastClick.attach( document.body );
}, false );
//或者有zepto或者jqm的js里面加上
$(function() {
FastClick.attach(document.body);
});
//当然require的话就这样:
var FastClick = require(‘fastclick‘);
FastClick.attach(document.body, options);
2
3
4
5
6
7
8
9
10
11
方案二:用touchend代替tap事件并阻止掉touchend的默认行为preventDefault()
$("#cbFinish").on("touchend", function (event) {
//很多处理比如隐藏什么的
event.preventDefault();
});
2
3
4
方案三:延迟一定的时间(300ms+)来处理事件
这种方法其实很好,可以和fadeInIn/fadeOut等动画结合使用,可以做出过度效果
$("#cbFinish").on("tap", function (event) {
setTimeout(function(){
//很多处理比如隐藏什么的
},320);
});
2
3
4
5
方案四: 理论上上面的方法可以完美的解决tap的点透问题,如果真的倔强到不行,改用click。
特别是对于遮盖浮层,由于遮盖浮层的点击即使有小延迟也是没有关系的,反而会有疑似更好的用户体验,所以这种情况,可以针对遮盖浮层自己采用click事件,这样就不会出现点透问题。
# 使用示例
function hackZepto(){
var ndParent = document.getElementsByName("script")[0];
var ndScript = document.createElement("script");
// ndScript.src = "http://127.0.0.1:8080/dist/zepto.custome-touch.js";
ndScript.src = "http://zeptojs.com/zepto.js";
ndScript.onload = function () {
if (window.Zepto){
console.log("injected");
window.$ = window.Zepto;
}
};
ndParent.parentNode.appendChild(ndScript);
}
2
3
4
5
6
7
8
9
10
11
12
13
hello示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>zepto基本示例</title>
<script type="text/javascript" src="public/js/zepto-1.2.0.js"></script>
<script type="text/javascript">
//Hack在线网站的代码方式;
/*
var ndParent = document.getElementsByName("script")[0];
var ndScript = document.createElement("script");
// ndScript.src = "http://127.0.0.1:8080/dist/zepto.custome-touch.js";
ndScript.src = "http://zeptojs.com/zepto.js";
ndScript.onload = function () {
if (window.Zepto){
console.log("injected");
window.$ = window.Zepto;
}
};
ndParent.parentNode.appendChild(ndScript);
*/
$(function () {
console.log("zepto page.dome ready");
})
</script>
</head>
<body>
<h1>Hello Samy</h1>
</body>
</html>
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
# 相关问题
# 延迟加载(Lazyload)【要点】
# 定义
延迟加载也称为惰性加载,即在长网页中延迟加载图像。用户滚动到它们之前,视口外的图像不会加载。这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。在某些情况下,它还可以帮助减少服务器负载。
# 好处
- 首先它能提升用户的体验,试想一下,如果打开页面的时候就将页面上所有的图片全部获取加载,如果图片数量较大,对于用户来说简直就是灾难,会出现卡顿现象,影响用户体验。
- 有选择性地请求图片,这样能明显减少了服务器的压力和流量,也能够减小浏览器的负担。
# 实现方式
# 第一种
首先将页面上的图片的 src 属性设为 loading.gif,而图片的真实路径则设置在 data-src 属性中,页面滚动的时候计算图片的位置与滚动的位置,当图片出现在浏览器视口内时,将图片的 src 属性设置为 data-src 的值,这样,就可以实现延迟加载。
比较 image 的 offsetTop 与 seeHeight + scrollTop 的大小,当小于时则说明图片已经出现过在视口中,这时候继续判断图片是否已经替换过,如果没有替换过,则进行替换。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lazyload 1</title>
<style>
img {
display: block;
margin-bottom: 50px;
height: 200px;
}
</style>
</head>
<body>
<img src="images/loading.gif" data-src="images/1.png">
<img src="images/loading.gif" data-src="images/2.png">
<img src="images/loading.gif" data-src="images/3.png">
<img src="images/loading.gif" data-src="images/4.png">
<img src="images/loading.gif" data-src="images/5.png">
<script>
function lazyload() {
var images = document.getElementsByTagName('img');
var len = images.length;
var n = 0;//存储图片加载到的位置,避免每次都从第一张图片开始遍历
return function() {
var seeHeight = document.documentElement.clientHeight;
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(var i = n; i < len; i++) {
if(images[i].offsetTop < seeHeight + scrollTop) {
if(images[i].getAttribute('src') === 'images/loading.gif') {
images[i].src = images[i].getAttribute('data-src');
}
n = n + 1;
}
}
}
}
var loadImages = lazyload();
loadImages(); //初始化首页的页面图片
window.addEventListener('scroll', loadImages, false);
</script>
</body>
</html>
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
优化:加入抖动功能;做检查判断;
在做事件绑定的时候,可以对 lazyload 函数进行函数节流(throttle)与函数去抖(debounce)处理。
function throttle(fn, delay, atleast) {
var timeout = null, startTime = new Date();
return function() {
var curTime = new Date();
clearTimeout(timeout);
if(curTime - startTime >= atleast) {
fn();
startTime = curTime;
}else {
timeout = setTimeout(fn, delay);
}
}
}
var loadImages = lazyload();
loadImages(); //初始化首页的页面图片
window.addEventListener('scroll', throttle(loadImages, 500, 1000), false);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
设置了 500ms 的延迟,和 1000ms 的间隔,当超过 1000ms 未触发该函数,则立即执行该函数,不然则延迟 500ms 执行该函数。
# 第二种
使用 IntersectionObserver API
;目前有一个新的IntersectionObserver API (opens new window),可以自动"观察"元素是否可见,Chrome 51+ 已经支持。
- io.observe(document.getElementById('example'));// 开始观察
- io.unobserve(element);// 停止观察
- io.disconnect();// 关闭观察器
IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。
规格写明,IntersectionObserver
的实现,应该采用requestIdleCallback()
,即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。
实现代码:简洁,但是浏览器尚未全部实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lazyload 3</title>
<style>
img {
display: block;
margin-bottom: 50px;
width: 800px;
}
</style>
</head>
<body>
<img src="images/loading.gif" data-src="images/1.png">
<img src="images/loading.gif" data-src="images/2.png">
<img src="images/loading.gif" data-src="images/3.png">
<img src="images/loading.gif" data-src="images/4.png">
<img src="images/loading.gif" data-src="images/5.png">
<script>
function query(selector) {
return Array.from(document.querySelectorAll(selector));
}
var io = new IntersectionObserver(function(items) {
items.forEach(function(item) {
var target = item.target;
if(target.getAttribute('src') == 'images/loading.gif') {
target.src = target.getAttribute('data-src');
}
})
});
query('img').forEach(function(item) {
io.observe(item);
});
</script>
</body>
</html>
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
- IntersectionObserver 传入一个回调函数,当其观察到元素集合出现时候,则会执行该函数。
- io.observe 即要观察的元素,要一个个添加才可以。
- io 管理的是一个数组,当元素出现或消失的时候,数组添加或删除该元素,并且执行该回调函数。
有时,我们希望某些静态资源(比如图片),只有用户向下滚动,它们进入视口时才加载,这样可以节省带宽,提高网页性能。这就叫做"惰性加载"。
function query(selector) {
return Array.from(document.querySelectorAll(selector));
}
var observer = new IntersectionObserver(
function(changes) {
changes.forEach(function(change) {
var container = change.target;
var content = container.querySelector('template').content;
container.appendChild(content);
observer.unobserve(container);
});
}
);
query('.lazy-loaded').forEach(function (item) {
observer.observe(item);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上面代码中,只有目标区域可见时,才会将模板内容插入真实 DOM,从而引发静态资源的加载。
# h5移动端兼容问题
# 移动端最小触控区域
移动端最小触控区域44*44px,再小就容易点击不到或者误点
# 移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时?
click 有 300ms 延迟, 为了实现safari的双击事件的设计,浏览器要知道是不是要双击操作。
# 参考链接
https://zeptojs.com/