我爱编程

js Jquery

2017-05-24  本文已影响414人  时光慢慢些

JQuery的源码看过吗?能不能简单概况一下它的实现原理?

jQuery.fn的init方法返回的this指的是什么对象?为什么要返回this?

(function(window, undefined) {
    var
    // ...
    jQuery = function(selector, context) {
        // The jQuery object is actually just the init constructor 'enhanced'
        // 看这里,实例化方法 jQuery() 实际上是调用了其拓展的原型方法 jQuery.fn.init
        return new jQuery.fn.init(selector, context, rootjQuery);
    },
 
    // jQuery.prototype 即是 jQuery 的原型,挂载在上面的方法,即可让所有生成的 jQuery 对象使用
    jQuery.fn = jQuery.prototype = {
        // 实例化化方法,这个方法可以称作 jQuery 对象构造器
        init: function(selector, context, rootjQuery) {
            // ...
        }
    }
    // 这一句很关键,也很绕
    // jQuery 没有使用 new 运算符将 jQuery 实例化,而是直接调用其函数
    // 要实现这样,那么 jQuery 就要看成一个类,且返回一个正确的实例
    // 且实例还要能正确访问 jQuery 类原型上的属性与方法
    // jQuery 的方式是通过原型传递解决问题,把 jQuery 的原型传递给jQuery.prototype.init.prototype
    // 所以通过这个方法生成的实例 this 所指向的仍然是 jQuery.fn,所以能正确访问 jQuery 类原型上的属性与方法
    jQuery.fn.init.prototype = jQuery.fn;
 
})(window);

大部分人初看 jQuery.fn.init.prototype = jQuery.fn 这一句都会被卡主,很是不解。但是这句真的算是 jQuery 的绝妙之处。理解这几句很重要,分点解析一下:

1)首先要明确,使用 $('xxx') 这种实例化方式,其内部调用的是 return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了 jQuery.fn.init() 方法去完成。

2)将 jQuery.fn.init 的 prototype 属性设置为 jQuery.fn,那么使用 new jQuery.fn.init() 生成的对象的原型对象就是 jQuery.fn ,所以挂载到 jQuery.fn 上面的函数就相当于挂载到 jQuery.fn.init() 生成的 jQuery 对象上,所有使用 new jQuery.fn.init() 生成的对象也能够访问到 jQuery.fn 上的所有原型方法。

3)也就是实例化方法存在这么一个关系链

jquery中如何将数组转化为json字符串,然后再转化回来?

jQuery中没有提供这个功能,所以你需要先编写两个jQuery的扩展:

  $.fn.stringifyArray = function(array) {
        return JSON.stringify(array)
    }

    $.fn.parseArray = function(array) {
        return JSON.parse(array)
    }

    //然后调用:
    var array = [1,2,3,4];
    $.fn.stringifyArray(array)
    $.fn.parseArray($.fn.stringifyArray(array))

jQuery的属性拷贝(extend)的实现原理是什么,如何实现深拷贝?

语法:jQuery.extend( [deep ], target, object1 [, objectN ] )

在默认情况下,通过$.extend()合并操作不是递归的(浅拷贝);如果第一个对象的属性本身是一个对象或数组,那么它将完全用第二个对象相同的key重写一个属性。这些值不会被合并。然而,如果将 true 作为该函数的第一个参数,那么会在对象上进行递归的合并(深拷贝)。

浅拷贝(false 默认):如果第二个参数对象有的属性第一个参数对象也有,那么不会进行相同参数内部的比较,直接将第一个对象的相同参数覆盖。

深拷贝(true):如果第二个参数对象有的属性第一个参数对象也有,还要继续在这个相同的参数向下一层找,比较相同参数的对象中是否还有不一样的属性,如果有,将其继承到第一个对象,如果没有,则覆盖。

var object1 = {
    apple: 0,
    banana: {
        weight: 52,
        price: 100
    },
    cherry: 97
};
var object2 = {
    banana: {
        price: 200
    },
    durian: 100
};

//默认情况浅拷贝
//object1--->{"apple":0,"banana":{"price":200},"cherry":97,"durian":100}
//object2的banner覆盖了object1的banner,但是weight属性未被继承
//$.extend(object1, object2);

//深拷贝
//object1--->{"apple":0,"banana":{"weight":52,"price":200},"cherry":97,"durian":100}
//object2的banner覆盖了object1的banner,但是weight属性也被继承了呦
$.extend(true,object1, object2);

console.log('object1--->'+JSON.stringify(object1));

jquery.extend与jquery.fn.extend的区别?

jquery.extend(object); 为扩展jQuery类本身.为类添加新的方法。
jquery.fn.extend(object);给jQuery对象添加方法。

1.jQuery添加一个为 add的“静态方法”,之后便可以在引入 jQuery 的地方,使用这个方法了.

$.extend({ 
  add:function(a,b){return a+b;} 
}); 
 
//$.add(3,4);
//return 7 

2.jQuery.fn.extend(object); 对jQuery.prototype进得扩展,就是为jQuery类添加“成员函数”。jQuery类的实例可以使用这个“成员函数”。

$.fn.extend({ 
  alertClick:function(){ 
    $(this).click(function(){ 
      alert($(this).val()); 
    }); 
  } 
}); 
 
//页面上为:
<input id="input1" type="text"/>    
 
//使用
$("#input1").alertClick();  

jQuery的队列是如何实现的?队列可以用在哪些地方?

queue() 方法操作在匹配元素上执行的函数队列。
.queue(queueName,newQueue)
queueName 可选。字符串值,包含序列的名称。默认是 fx, 标准的效果序列。

每个元素均可拥有一到多个由 jQuery 添加的函数队列。在大多数应用程序中,只使用一个队列(名为 fx)。队列运行在元素上异步地调用动作序列,而不会终止程序执行。典型例子时调用元素上的多个动画方法。例如:
$('#foo').slideUp().fadeIn();
当这条语句执行时,元素会立即开始其滑动动画,但是淡入过渡被置于 fx 队列,只有当滑动过渡完成后才会被调用。

.queue() 方法允许我们直接对这个函数队列进行操作。调用带有回调函数的 .queue() 方法特别有用;它允许我们在队列末端放置一个新函数。

这个特性与动画方法提供回调函数类似,但是无需在动画执行时设置回调函数。

$('#foo').slideUp();
$('#foo').queue(function() {
  alert('Animation complete.');
  $(this).dequeue();
});

等价于:

$('#foo').slideUp(function() {
  alert('Animation complete.');
});

请注意,当通过 .queue() 添加函数时,我们应当确保最终调用了 .dequeue(),这样下一个排队的函数才能执行。

例子 1

对自定义函数进行队列操作:

$(document.body).click(function () {
  $("div").show("slow");
  $("div").animate({left:'+=200'},2000);
  $("div").queue(function () {
    $(this).addClass("newcolor");
    $(this).dequeue();
  });
  $("div").animate({left:'-=200'},500);
  $("div").queue(function () {
    $(this).removeClass("newcolor");
    $(this).dequeue();
  });
  $("div").slideUp();
});
例子 2

设置队列数组来删除队列:

$("#start").click(function () {
  $("div").show("slow");
  $("div").animate({left:'+=200'},5000);
  $("div").queue(function () {
    $(this).addClass("newcolor");
    $(this).dequeue();
  });
  $("div").animate({left:'-=200'},1500);
  $("div").queue(function () {
    $(this).removeClass("newcolor");
    $(this).dequeue();
  });
  $("div").slideUp();
});

$("#stop").click(function () {
  $("div").queue("fx", []);
  $("div").stop();
});

谈一下Jquery中的bind(),live(),delegate(),on()的区别?

$(selector).bind(event,data,function)
$(selector).live(event,data,function)//jquery1.9版本以下支持,jquery1.9及其以上版本删除了此方法,jquery1.9以上版本用on()方法来代替
$(selector).delegate(childSelector,event,data,function)//jquery1.4.2及其以上版本;
$(selector).on(event,childselector,data,function)//jquery1.7及其以上版本;jquery1.7版本出现之后用于替代bind(),live()绑定事件方式;

event:必需项;添加到元素的一个或多个事件,例如 click,dblclick等;
单事件处理:例如 $(selector).bind("click",data,function);
多事件处理:

  1. 利用空格分隔多事件,例如 $(selector).bind("click dbclick mouseout",data,function);
  2. 利用大括号灵活定义多事件,例如 $(selector).bind({event1:function, event2:function, ...})

空格相隔方式:绑定较为死板,不能给事件单独绑定函数,适合处理多个事件调用同一函数情况;
大括号替代方式:绑定较为灵活,可以给事件单独绑定函数;

data:可选;需要传递的参数;
function:必需;当绑定事件发生时,需要执行的函数;适用所有版本,但是根据官网解释,自从jquery1.7版本以后bind()函数推荐用on()来代替。

1、.bind()是直接绑定在元素上,也很好的解决了浏览器在事件处理中的兼容问题。如果不人为的设置stopPropagation(Moder Browser), cancelBubble(IE),那么它的所有父元素,祖宗元素都会受之影响

$('a').bind('click', function() { alert("That tickles!") });

当我们在a 上面点击的时候,首先会触发它本身所绑定的click事件,然后会一路往上,触发它的父元素,祖先元素上所有绑定的click事件。

/* The .bind() method attaches the event handler directly to the DOM
element in question ( "#members li a" ). The .click() method is
just a shorthand way to write the .bind() method. */

$( "#members li a" ).bind( "click", function( e ) {} ); 
$( "#members li a" ).click( function( e ) {} );  

.click(), .hover()...这些非常方便的事件绑定,都是bind的一种简化处理方式。对于利用ID选出来的元素是非常好的,不仅仅是很快的可以hook上去(因为一个页面只有一个id),而且当事件发生时,handler可以立即被执行(相对于后面的live, delegate)实现方式
缺点:

它会绑定事件到所有的选出来的元素上
它不会绑定到在它执行完后动态添加的那些元素上
当元素很多时,会出现效率问题
当页面加载完的时候,你才可以进行bind(),所以可能产生效率问题

2、.live()则是通过冒泡的方式来绑定到元素上的。更适合列表类型的,绑定到document DOM节点上。一旦事件冒泡到document上,jQuery将会查找selector/event metadata,然后决定那个handler应该被调用。当handler在执行的时候,因为有冒泡的参与,确实会有一些延迟,但是绑定的时候是特别的快。和.bind()相比的时候有一个好处就是我们不需要在每个元素上再去绑定事件,而只在document上绑定一次就可以了。尽管这个不是最快的方式,但是确实是最少浪费的。

优点:

这里仅有一次的事件绑定,绑定到document上而不像.bind()那样给所有的元素挨个绑定
那些动态添加的elemtns依然可以触发那些早先绑定的事件,因为事件真正的绑定是在document上
你可以在document ready之前就可以绑定那些需要的事件

缺点:

从1.7开始已经不被推荐了,所以你也要开始逐步淘汰它了。
Chaining没有被正确的支持
当使用event.stopPropagation()是没用的,因为都要到达document
因为所有的selector/event都被绑定到document, 所以当我们使用matchSelector方法来选出那个事件被调用时,会非常慢
当发生事件的元素在你的DOM树中很深的时候,会有performance问题

3、.delegate()则是更精确的小范围使用事件代理,性能优于.live()。它不会把所有的event全部绑定到document,而是由你决定把它放在哪儿。而和.live()相同的地方在于都是用event delegation.
优点:

你可以选择你把这个事件放到那个元素上了

chaining被正确的支持了
jQuery仍然需要迭代查找所有的selector/event data来决定那个子元素来匹配,但是因为你可以决定放在那个根元素上,所以可以有效的减小你所要查找的元素。
可以用在动态添加的元素上

缺点:

需要查找那个那个元素上发生了那个事件了,尽管比document少很多了,不过,你还是得浪费时间来查找。

4、.on()则是最新的1.9版本整合了之前的三种方式的新事件绑定机制。.bind(), .live(), .delegate()都是通过.on()来实现的,.unbind(), .die(), .undelegate(),也是一样的都是通过.off()来实现的。

JQuery一个对象可以同时绑定多个事件,这是如何实现的?

$ele.on('eventName', handle1);
$ele.on('eventName', handle2);
$ele.on('eventName', handle3);

其实$ele元素的eventName事件有一个处理函数数组 监听一次就往里面放一个handle, 数组是先进后出型的也就是栈, 然后触发事件的时候一次执行

上面的监听相当于

$ele.eventHandle['eventName'] = [];
$ele.eventHandle['eventName'].push(handle1);
$ele.eventHandle['eventName'].push(handle2);
$ele.eventHandle['eventName'].push(handle3);

然后$ele.trigger('eventName')触发的时候, 从栈里面取出处理函数执行

while($ele.eventHandle['eventName'].length) {
handle = $ele.eventHandle['eventName'].pop();
handle();
}

最先监听的最后执行

是否知道自定义事件

一、jQuery自定义事件

jQuery的自定义事件是通过on和one绑定的,然后再通过trigger来触发这个事件

如有三种情况需要分别处理:

用户提交空值
用户提交的用户名不存在
用户提交的用户名存在

jQuery 提供的自定义事件可以引入语义,很好地解决问题

//1. 定义自定义事件
$('#username').on('blank.username', function() {
 console.log('请不要留空');
});
$('#username').on('notExist.username', function() {
 console.log('用户名不存在');
});
$('#username').on('success.username', function() {
 console.log('用户名存在');
});

//2. 触发自定义事件
$('.js-submit').on('click', function() {
 var username = $('#username').val();
 username = $.trim(username);
 if (username === '') {
 $('#username').trigger('blank.username'); // 如果 username 为空值,则触发 blank.username 事件
 }
 $.post(url, {username: username}, function(data) {
 var res = data;
 if (res.retcode === -1) {
 $('#username').trigger('notExist.username'); // 如果用户不存在,则触发 notExist.username 事件
 } else if (res.retcode === 0) {
 $('#username').trigger('success.username'); // 如果用户存在,则触发 sucess.username 事件
 }
 });
});

trigger需要处理的问题

1.模拟事件对象,用户模拟处理停止事件冒泡

triger()方法触发事件后,会执行浏览器默认操作。例如:

$("input").trigger("focus");

以上代码不仅会触发为input元素绑定的focus事件,也会使input元素本身得到焦点(浏览器默认操作)。

如果只想触发绑定的focus事件,而不想执行浏览器默认操作,可以使用jQuery中另一个类似的非冒泡式方法-triggerHandler()方法。

$container.one("focus",function(){
.....
});
$("input").triggerHandler("focus");

该方法会触发input元素上绑定的特定事件,同时取消浏览器对此事件的默认操作,即文本框指触发绑定的focus事件,不会得到焦点。

请注意这里使用了jQuery 的one 来代替on。这两者的区别在于,one 在触发处理器之后会自动将其删除。

2.区分事件类型,触发标准的浏览器事件 和 自定义事件名绑定的处理程序。

解决方法:事件名称+命名空间

p4.on('click.aaa.ccc',function(e,vv,c){
       console.log('p4')
   })
p4.trigger('click.aaa')
二、javascript的自定义事件
  1. 简单的自定义事件

自定义事件到激发这个事件,需要document.createEvent(),event.initEvent(),element.dispatchEvent()这三部,分别是创建事件对象,初始化事件对象,触发事件

<div id="testBox"></div>
// 1. 创建事件
var evt = document.createEvent('HTMLEvents');
// 2. 定义事件类型,事件初始化
evt.initEvent('customEvent', true, true);
// 3. 在元素上监听事件,绑定监听
var obj = document.getElementById('testBox');
obj.addEventListener('customEvent', function(){
    console.log('customEvent 事件触发了'+event.type);
}, false);

console 中输入 obj.dispatchEvent(evt),可以看到 console 中输出“customEvent 事件触发了”,表示自定义事件成功触发
遗憾的是在 IE8 及以下版本的 IE 中并不支持document.createEvent()的方法,IE支持的 document.createEventObject()和event.fireEvent()方法,但是经过测试,fireEvent并不能用于自定义事件,传给它的参数只能是在IE已经定义了的事件,fireEvent 只支持标准事件的触发。

function foo1(){
        addLog("foo1 is excute");
}
function foo2(){
        addLog("the id is "+idChange.getId()+" now!");
}
if(document.createEvent){ //This is for the stand browser.
        var ev=document.createEvent('HTMLEvents');
        ev.initEvent('fakeEvent',false,false);
        document.addEventListener("fakeEvent",foo1,false);
        document.addEventListener("fakeEvent",foo2,false);
    }else if(document.attachEvent){     //This is for the damn IE
        document.documentElement.fakeEvents = 0; // an expando property
        document.documentElement.attachEvent("onpropertychange", function(event) {
            if (event.propertyName == "fakeEvents") {
                foo1();
            }
        });
        document.documentElement.attachEvent("onpropertychange",function(event){
            if(event.propertyName == "fakeEvents"){
                foo2();
            }
        });
}
function addLog(log){
        var logDiv=document.getElementById('log');
        var p=document.createElement("p");
        p.appendChild(document.createTextNode(log));
        logDiv.appendChild(p);
}
var idChange=function(){
        var id=1;
        return {getId:function(){return id;},
                setId:function(a){
               id=a;
               if(document.dispatchEvent) document.dispatchEvent(ev);
           else if(document.attachEvent)     document.documentElement.fakeEvents++; //This for IE
                    }}
}();

http://www.cnblogs.com/JoannaQ/p/4415947.html

jQuery里的fire函数是什么意思,什么时候用?

定义和用法
callbacks.fire() 函数用于传入指定的参数调用所有的回调。
此方法返回一个回调对象到它绑定的回调列表。

// a sample logging function to be added to a callbacks list
var foo = function( value ){
    console.log( 'foo:' + value );
}

var callbacks = $.Callbacks();

// add the function 'foo' to the list
callbacks.add( foo );

// fire the items on the list
callbacks.fire( 'hello' ); // outputs: 'foo: hello'
callbacks.fire( 'world '); // outputs: 'foo: world'

// add another function to the list
var bar = function( value ){
    console.log( 'bar:' + value );
} 

// add this function to the list
callbacks.add( bar );

// fire the items on the list again
callbacks.fire( 'hello again' );
// outputs:
// 'foo: hello again'
// 'bar: hello again'

jQuery是通过哪个方法和Sizzle选择器结合的?

jQuery.fn.find()进入Sizzle

针对jQuery性能的优化方法?

=================================================================

一、选择器性能优化建议

=================================================================

1. 总是从#id选择器来继承
这是jQuery选择器的一条黄金法则。jQuery选择一个元素最快的方法就是用ID来选择了。

$(‘#content’).hide();

或者从ID选择器继承来选择多个元素:

$(‘#content p’).hide();

2. 在class前面使用tag
jQuery中第二快的选择器就是tag选择器(如$(‘head’)),因为它和直接来自于原生的JavaScript方法getElementByTagName()。所以最好总是用tag来修饰class(并且不要忘了就近的ID)

varreceiveNewsletter = $(‘#nslForm input.on’);

jQuery中class选择器是最慢的,因为在IE 浏览器 下它会遍历所有的DOM节点。尽量避免使用class选择器。也不要用tag来修饰ID。下面的例子会遍历所有的div元素来查找id为’content’的那个节点:

  varcontent = $(‘div#content’);// 非常慢,不要使用

用ID来修饰ID也是画蛇添足:

vartraffic_light = $(‘#content #traffic_light’);// 非常慢,不要使用

3. 使用子查询
将父对象缓存起来以备将来的使用

varheader = $(‘#header’);
varmenu = header.find(‘.menu’);
// 或者
varmenu = $(‘.menu’, header);

4. 优化选择器以适用Sizzle的“从右至左”模型
自版本1.3之后,jQuery采用了Sizzle库,与之前的版本在选择器引擎上的表现形式有很大的不同。它用“从左至右”的模型代替了“从右至左”的模型。确保最右的选择器具体些,而左边的选择器选择范围较宽泛些:

  var linkContacts = $(‘.contact-links div.side-wrapper’);
  而不要使用
  var linkContacts = $(‘a.contact-links .side-wrapper’);

5. 采用find(),而不使用上下文查找
.find()函数的确快些。但是如果一个页面有许多DOM节点时,需要来回查找时,可能需要更多时间:

  vardivs = $(‘.testdiv’,’#pageBody’);// 2353 on Firebug 3.6
  vardivs = $(‘#pageBody’).find(‘.testdiv’);// 2324 on Firebug 3.6 – The best time
  vardivs = $(‘#pageBody .testdiv’);// 2469 on Firebug 3.6

6. 利用强大的链式操作
采用jQuery的链式操作比缓存选择器更有效:

$(‘li.menu-item’).click(function() {alert(‘test click’);})
.css(‘display’,’block’)
.css(‘color’,’red’)
fadeTo(2, 0.7);

记住,永远不要让相同的选择器在你的代码里出现多次:
(1)为了区分普通的JavaScript对象和jQuery对象,可以在变量首字母前加上$符号。

7. 编写属于你的选择器
如果你经常在代码中使用选择器,那么扩展jQuery的$.expr[‘:’]对象吧,编写你自己的选择器。下面的例子中,我创建了一个abovethefold选择器,用来选择不可见的元素:

$.extend($.expr[‘:’], {
abovethefold:function(el) {
return$(el).offset().top < $(window).scrollTop() + $(window).height();
}
});
varnonVisibleElements = $(‘div:abovethefold’);// 选择元素

=================================================================

二、优化DOM操作建议

=================================================================

8. 缓存jQuery对象
将你经常用的元素缓存起来:

 varheader = $(‘#header’);
vardivs = header.find(‘div’);
varforms = header.find(‘form’);

9. 当要进行DOM插入时,将所有元素封装成一个元素
这里的基本思想是在内存中建立你确实想要的东西,然后更新DOM。这并不是一个jQuery最佳实践,但必须进行有效的JavaScript操作 。直接的DOM操作速度很慢
直接的DOM操作很慢。尽可能少的去更改HTML结构。

varmenu = ‘<ul id=”menu”>’;
for(vari = 1; i < 100; i++) {
menu += ‘<li>’+ i + ‘</li>’;
}
menu += ‘</ul>’;
$(‘#header’).prepend(menu);
// 千万不要这样做:
$(‘#header’).prepend(‘<ul id=”menu”></ul>’);
for(vari = 1; i < 100; i++) {
$(‘#menu’).append(‘<li>’+ i + ‘</li>’);
}

冒泡事件: 除非在特殊情况下,
否则每一个js事件(例如:click,
mouseover等.)都会冒泡到父级节点。当我们需要给多个元素调用同个函数时这点会很有用。代替这种效率很差的多元素事件监听的方法就是,
你只需向它们的父节点绑定一次。 比如, 我们要为一个拥有很多输入框的表单绑定这样的行为:
当输入框被选中时为它添加一个class传统的做法是,直接选中input,然后绑定focus等,如下所示:

$(“#entryform input”).bind(“focus”,function(){
    $(this).addClass(“selected”);
}).bind(“blur”,function(){
    $(this).removeClass(“selected”);
});

当然上面代码能帮我们完成相应的任务,但如果你要寻求更高效的方法,请使用如下代码:

$(“#entryform”).bind(“focus”,function(e){
    var$cell = $(e.target); // e.target 捕捉到触发的目标元素
    $cell.addClass(“selected”);
}).bind(“blur”,function(e){
    var$cell = $(e.target);
    $cell.removeClass(“selected”);
});

通过在父级监听获取焦点和失去焦点的事件,对目标元素进行操作。在上面代码中,父级元素扮演了一个调度员的角色,
它可以基于目标元素绑定事件。如果你发现你给很多元素绑定了同一个事件监听,
那么现在的你肯定知道哪里做错了。同理,在Table操作时,我们也可以使用这种方式加以改进代码:普通的方式

$(‘#myTable td’).click(function(){
    $(this).css(‘background’,’red’);
});
//改进方式:
$(‘#myTable’).click(function(e) {
var$clicked = $(e.target);
$clicked.css(‘background’,’red’);
});

假设有100个td,在使用普通的方式的时候,你绑定了100个事件。在改进方式中,你只为一个元素绑定了1个事件,至于是100个事件的效率高,还是1个事件的效率高,相信你也能自行分辨了。

10. 尽管jQuery不会抛出异常,但开发者也应该检查对象
尽管jQuery不会抛出大量的异常给用户,但是开发者也不要依赖于此。jQuery通常会执行了一大堆没用的函数之后才确定一个对象是否存在。所以在对一个作一系列引用之前,应先检查一下这个对象存不存在。

11. 使用直接函数,而不要使用与与之等同的函数
为了获得更好的性能,你应该使用直接函数如$.ajax(),而不要使用$.get(),$.getJSON(),$.post(),因为后面的几个将会调用$.ajax()。

12. 缓存jQuery结果,以备后来使用
你经常会获得一个javasript应用对象——你可以用App.来保存你经常选择的对象,以备将来使用:

App.hiddenDivs = $(‘div.hidden’);
// 之后在你的应用中调用:
App.hiddenDivs.find(‘span’);

13. 采用jQuery的内部函数data()来存储状态
不要忘了采用.data()函数来存储信息:

$(‘#head’).data(‘name’,’value’);
// 之后在你的应用中调用:
$(‘#head’).data(‘name’);

14. 使用jQuery utility函数
不要忘了简单实用的jQuery的 utility函数 。我最喜欢的是$.isFunction(),$isArray()和$.each()。

15. 为HTML块添加“JS”的class
当jQuery载入之后,首先给HTML添加一个叫”JS”的class

$(‘HTML’).addClass(‘JS’);

只有当用户启用JavaScript的时候,你才能添加CSS样式。例如

/* 在css中 */
.JS #myDiv{display:none;}

所以当JavaScript启用的时候,你可以将整个HTML内容隐藏起来,用jQuery来实现你想实现的(譬如:收起某些面板或当用户点击它们时展开)。而当Javascript没有启用的时候,浏览器呈现所有的内容,搜索引擎爬虫也会勾去所有内容。我将来会更多的使用这个技巧。

=================================================================

三、关于优化事件性能的建议

=================================================================

16. 推迟到$(window).load
有时候采用$(window).load()比$(document).ready()更快,因为后者不等所有的DOM元素都下载完之前执行。你应该在使用它之前测试它。

17. 使用Event Delegation
当你在一个容器中有许多节点,你想对所有的节点都绑定一个事件,delegation很适合这样的应用场景。使用Delegation,我们仅需要在父级绑定事件,然后查看哪个子节点(目标节点)触发了事件。当你有一个很多数据的table的时候,你想对td节点设置事件,这就变得很方便。先获得
table,然后为所有的td节点设置delegation事件:

$(“table”).delegate(“td”,”hover”,function(){
$(this).toggleClass(“hover”);
});

18.使用ready事件的简写
如果你想压缩js插件,节约每一个字节,你应该避免使用$(document).onready()

/ 也不要使用
$(document).ready(function(){
// 代码
});
// 你可以如此简写:
$(function(){
// 代码
});

=================================================================

四、测试jQuery

=================================================================

19. jQuery单元测试
测试JavaSript代码最好的方法就是人来测试。但你可以使用一些自动化的工具如 SeleniumFuncunitQUitQMock 来测试你的代码(尤其是插件)。我想在另外一个专题来讨论这个话题因为实在有太多要说的了。

20. 标准化你的jQuery代码
经常标准化你的代码,看看哪个查询比较慢,然后替换它。你可以用Firebug控制台。你也可以使用 jQuery的快捷函数 来使测试变得更容易些:

// 在Firebug控制台记录数据的快捷方式
$.l($(‘div’));
// 获取UNIX时间戳
$.time();
// 在Firebug记录执行代码时间
$.lt();
$(‘div’);
$.lt();
// 将代码块放在一个for循环中测试执行时间
$.bm(“var divs = $(‘.testdiv’, ‘#pageBody’);”);// 2353 on Firebug 3.6

=================================================================

五、其他常用jQuery性能优化建议

=================================================================

21. 使用最新版本的jQuery
最新的版本往往是最好的。更换了版本后,不要忘记测试你的代码。有时候也不是完全向后兼容的。

22. 使用HMTL5
新的HTML5标准带来的是更轻巧的DOM结构。更轻巧的结构意味着使用jQuery需要更少的遍历,以及更优良的载入性能。所以如果可能的话请使用HTML5。

23. 如果给15个以上的元素加样式时,直接给DOM元素添加style标签
要给少数的元素加样式,最好的方法就是使用jQuey的css()函数。然而更15个以上的较多的元素添加样式时,直接给DOM添加style 标签更有效些。这个方法可以避免在代码中使用硬编码(hard code)。

$(‘<style type=”text/css”> div.class { color: red; } </style>’).appendTo(‘head’);

24. 避免载入多余的代码
将Javascript代码放在不同的文件中是个好的方法,仅在需要的时候载入它们。这样你不会载入不必要的代码和选择器。也便于管理代码。

25. 压缩成一个主JS文件,将下载次数保持到最少
当你已经确定了哪些文件是应该被载入的,那么将它们打包成一个文件。用一些开源的工具可以自动帮你完成,如使用 Minify (和你的后端代码集成)或者使用JSCompressorYUI
Compressor
Dean Edwards JS packer 等在线工具可以为你压缩文件。我最喜欢的是 JSCompressor

26. 需要的时候使用原生的Javasript
使用jQuery是个很棒的事情,但是不要忘了它也是Javascript的一个框架。所以你可以在jQuery代码有必要的时候也使用原生的Javascript函数,这样能获得更好的性能。

27. 从Google载入jQuery框架
当你的应用正式上线的时候,请从Google CDN载入jQuery,因为用户可以从最近的地方获取代码。这样你可以减少服务器请求,而用户如果浏览其他网站,而它也使用Google CDN的jQuery时,浏览器就会立即从缓存中调出jQuery代码。

// 链接特定版本的压缩代码
<script
type=”text/javascript”src=”[color=blue
!important][url=https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js]https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js[/url]”></script>

复制代码

2 **8. 缓慢载入内容不仅能提高载入速度,也能提高SEO优化 ** (Lazy load content for speed and SEO benefits)
使用Ajax来载入你的网站吧,这样可以节约服务器端载入时间。你可以从一个常见的侧边栏widget开始。

Jquery与jQuery UI有啥区别?

(1) jQuery是一个js库,主要提供的功能是选择器,属性修改和事件绑定等等。  
(2) jQueryUI则是在jQuery的基础上,利用jQuery的扩展性,设计的插件。提供了一些常用的界面元素,诸如对话框、拖动行为、改变大小行为等等。 
(3) jQuery本身注重于后台,没有漂亮的界面,而jQueryUI则补充了前者的不足,他提供了华丽的展示界面,使人更容易接受。既有强大的后台,又有华丽的前台。

jQuery和Zepto的区别?各自的使用场景?

1.Zepto对象 不能自定义事件
2.Zepto 的选择器表达式: [name=value] 中value 必须用 双引号 ” or 单引号 ’ 括起来
3.Zepto 是根据标准浏览器写的,所以对于节点尺寸的方法只提供 width() 和 height(),省去了 innerWidth(), innerHeight(),outerWidth(),outerHeight()
4.offset()
Zepto.js: 返回 top 、 left 、 width 、 height
jQuery: 返回 width 、 height
5.Zepto 的each 方法只能遍历 数组,不能遍历JSON对象
6.zepto的jsonp callback函数名无法自定义
7.DOM 操作区别 jQuery 操作 ul 上的 id 不会被添加。
8.增加tap事件

Zepto最初是为移动端开发的库,是jQuery的轻量级替代品,因为它的API和jQuery相似,而文件更小,对任何项目都是个不错的选择。Zepto最大的优势是它的文件大小,只有8k多一点,是目前功能完备的库中最小的一个,尽管不大,Zepto所提供的工具足以满足开发程序的需要。大多数在jQuery中·常用的API和和方法Zepto都有,Zepto中还有一些jQuery中没有的,而用一些定制的JavaScript就很容易做出来的API和方法。另外,因为Zepto的API大部分都能和jQuery兼容,所以用起来极其容易,如果熟悉jQuery,就能很容易掌握Zepto。你可用同样的方式重用jQuery中的很多方法,也可以方面地把方法串在一起得到更简洁的代码,甚至不用看它的文档。
Zepto是不支持IE浏览器的,这不是Zepto的开发者Thomas Fucks在跨浏览器问题上犯了迷糊,而是经过了认真考虑后为了降低文件尺寸而做出的决定,就像jQuery的团队在2.0版中不再支持旧版的IE一样

Zepto的点透问题如何解决?

当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)上手指触摸开始,且手指未曾在屏幕上移动(某些<a href=”http://www.jd.com”>浏览器</a>允许移动一个非常小的位移值),且在这个DOM上手指离开屏幕,且触摸和离开屏幕之间的间隔时间较短(某些浏览器不检测间隔时间,也会触发click)才能触发

也就是说,在移动端事件的触发时间按由早到晚排列为:touchstart 早于 touchend 早于 click。亦即click的触发是有延迟的,这个时间大概在300ms左右。

由于我们在touchstart阶段就已经隐藏了罩层A,当click被触发时候,能够被点击的元素则是其下的B元素,根据click事件的触发规则:只有在被触发时,当前有click事件的元素显示,且在面朝用户的最前端时,才触发click事件。由于B绑定了click事件(或者B本身默认存在click事件),所以B的click事件被触发,产生了点透的情况。

解决方案一:来得很直接github上有个fastclick可以完美解决https://github.com/ftlabs/fastclick

引入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);

解决方案二:

对于B元素本身存在默认click事件的情况,应及用touchend代替tap事件并阻止掉时A元素touchend的默认行为preventDefault(),从而阻止click事件的产生。

$("#aa").on("touchend", function (event) {
  //很多处理比如隐藏什么的
  event.preventDefault();
});

对于B元素本身没有默认click事件的情况(无a标签等),应统一使用touch事件,统一代码风格,并且由于click事件在移动端的延迟要大很多,不利于用户体验,所以关于触摸事件应尽量使用touch相关事件。

解决方案三:延迟一定的时间(300ms+)来处理事件

 $("#aa").on("tap", function (event) {
     setTimeout(function(){
     //很多处理比如隐藏什么的
     },320);
 });   

这种方法其实很好,可以和fadeInIn/fadeOut等动画结合使用,可以做出过度效果

解决方案四: 理论上上面的方法可以完美的解决tap的点透问题,如果真的倔强到不行,改用click。特别是对于遮盖浮层,由于遮盖浮层的点击即使有小延迟也是没有关系的,反而会有疑似更好的用户体验,所以这种情况,可以针对遮盖浮层自己采用click事件,这样就不会出现点透问题。

fastclick.js 的原理

源码解析
attach方法:

FastClick.attach = function(layer) {
    'use strict';
    return new FastClick(layer);
};

在FastClick的构造函数中

this.onClick = function() { return FastClick.prototype.onClick.apply(self, arguments); };

    this.onMouse = function() { return FastClick.prototype.onMouse.apply(self, arguments); };

    this.onTouchStart = function() { return FastClick.prototype.onTouchStart.apply(self, arguments); };

    this.onTouchEnd = function() { return FastClick.prototype.onTouchEnd.apply(self, arguments); };

    this.onTouchCancel = function() { return FastClick.prototype.onTouchCancel.apply(self, arguments); };

    if (FastClick.notNeeded(layer)) {
        return;
    }
    if (this.deviceIsAndroid) {
        layer.addEventListener('mouseover', this.onMouse, true);
        layer.addEventListener('mousedown', this.onMouse, true);
        layer.addEventListener('mouseup', this.onMouse, true);
    }
    layer.addEventListener('click', this.onClick, true);
    layer.addEventListener('touchstart', this.onTouchStart, false);
    layer.addEventListener('touchend', this.onTouchEnd, false);
    layer.addEventListener('touchcancel', this.onTouchCancel, false);

也就是在document.body上绑定了click,touchstart,touchend,touchcancel事件。这里假设,我们的页面有一个button,绑定了click事件。当用户点击此button时,会先触发touchstart事件,这时,会冒泡到document.body中,于是就会执行:

FastClick.prototype.onTouchStart = function(event) {
    'use strict';
    var targetElement, touch, selection;

    if (event.targetTouches.length > 1) {
        return true;
    }
    targetElement = this.getTargetElementFromEventTarget(event.target);
    touch = event.targetTouches[0];

    if (this.deviceIsIOS) {

        selection = window.getSelection();
        if (selection.rangeCount && !selection.isCollapsed) {
            return true;
        }

        if (!this.deviceIsIOS4) {

            if (touch.identifier === this.lastTouchIdentifier) {
                event.preventDefault();
                return false;
            }

            this.lastTouchIdentifier = touch.identifier;    
            this.updateScrollParent(targetElement);
        }
    }
    this.trackingClick = true;
    this.trackingClickStart = event.timeStamp;
    this.targetElement = targetElement;
    this.touchStartX = touch.pageX;
    this.touchStartY = touch.pageY;
    if ((event.timeStamp - this.lastClickTime) < 200) {
        event.preventDefault();
    }
    return true;
};

这个回调函数主要做了以下事情:

  1. 获取我们当前触发touchstart的元素,这里是button。
  1. 然后将鼠标的信息记录了下来,记录鼠标的信息主要是为了在后面touchend触发时,根据这里得到的x、y判断是否为click。
  2. 触发touchend事件,然后冒泡到document.body上,执行以下代码:
FastClick.prototype.onTouchEnd = function(event) {
    'use strict';
    var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
    if (this.touchHasMoved(event) || (event.timeStamp - this.trackingClickStart) > 300) {
        this.trackingClick = false;
        this.targetElement = null;
    }
    if (!this.trackingClick) {
        return true;
    }
    if ((event.timeStamp - this.lastClickTime) < 200) {
        this.cancelNextClick = true;
        return true;
    }

    this.lastClickTime = event.timeStamp;
    trackingClickStart = this.trackingClickStart;
    this.trackingClick = false;
    this.trackingClickStart = 0;
    if (this.deviceIsIOSWithBadTarget) {
        touch = event.changedTouches[0];
        targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset);
    }
    targetTagName = targetElement.tagName.toLowerCase();
    if (targetTagName === ‘label‘) {
        forElement = this.findControl(targetElement);
        if (forElement) {
            this.focus(targetElement);
            if (this.deviceIsAndroid) {
                return false;
            }

            targetElement = forElement;
        }
    } else if (this.needsFocus(targetElement)) {
        if ((event.timeStamp - trackingClickStart) > 100 || (this.deviceIsIOS && window.top !== window && targetTagName === ‘input‘)) {
            this.targetElement = null;
            return false;
        }

        this.focus(targetElement);
        if (!this.deviceIsIOS4 || targetTagName !== 'select') {
            this.targetElement = null;
            event.preventDefault();
        }
        return false;
    }
    if (this.deviceIsIOS && !this.deviceIsIOS4) {
        scrollParent = targetElement.fastClickScrollParent;
        if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
            return true;
        }
    }
    if (!this.needsClick(targetElement)) {
        event.preventDefault();
        this.sendClick(targetElement, event);
    }
    return false;
};

注意上面的代码中,event.preventDefault();会阻止真实的click事件的触发,因此,在button上面的click事件不会触发。接下来,我们只需要查看sendClick方法。

FastClick.prototype.sendClick = function(targetElement, event) {
    'use strict';
    var clickEvent, touch;
    if (document.activeElement && document.activeElement !== targetElement) {
        document.activeElement.blur();
    }
    touch = event.changedTouches[0];

    clickEvent = document.createEvent(‘MouseEvents‘);
      
    clickEvent.initMouseEvent(‘click‘, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
    clickEvent.forwardedTouchEvent = true;
    targetElement.dispatchEvent(clickEvent);
};

在此方法中,会创建一个自定义的click事件,然后在button上立即触发,于是,button绑定的click的事件回调函数马上执行,因此就没有300ms延迟了。

上面的initMouseEvent方法的前三个参数的意思:1.事件类型,2.是否冒泡,3.是否阻止浏览器的默认行为。

自定义的click事件阻止了浏览器的默认行为,事件冒泡,于是执行document.body的click事件回调函数。代码如下:

FastClick.prototype.onClick = function(event) {
    'use strict';
    var permitted;
    if (this.trackingClick) {
        this.targetElement = null;
        this.trackingClick = false;
        return true;
    }
    if (event.target.type === ‘submit‘ && event.detail === 0) {
        return true;
    }
    permitted = this.onMouse(event);
    if (!permitted) {
        this.targetElement = null;
    }
    return permitted;
};

jQueryUI如何自定义组件?

编写jQueryUI插件(widget)
使用jQueryUI的widget来写插件,相比于基本的jquery插件有一些好处:

带来好处的同时也带来了荆棘和陷阱,本文的目的就是梳理这些荆棘,标出哪里有陷阱。
http://www.cnblogs.com/dc10101/archive/2012/05/03/2481004.html

jQuery的slideUp动画 ,如果目标元素是被外部事件驱动,当鼠标快速地连续触发外部元素事件,动画会滞后的反复执行,该如何处理呢?

$("#div").stop();//停止当前动画,继续下一个动画 
$("#div").stop(true);//清除元素的所有动画 
$("#div").stop(false, true);//让当前动画直接到达末状态 ,继续下一个动画
$("#div").stop(true, true);//清除元素的所有动画,让当前动画直接到达末状态
上一篇 下一篇

猜你喜欢

热点阅读