本书代码参考: Learning jQuery Code Listing Browserjavascript
原书: jQuery基础教程php
第4章 样式与动画web
2. 隐藏和显示元素json
$()函数实际上是建立了一个jQuery对象. 这个函数接受CSS选择符做为参数,充当一个工厂, 返回包含页面中对应元素的jQuery对象. 全部能在样式表中使用的选择符均可以传给这个函数, 随后就能够对匹配的元素集合应用jQuery方法.
在jQuery中,美圆符号$其实就是标示符jQuery的"别名".
1. 基本选择符 $('p') //取得全部标签为p的元素 $('.class') //取得全部类为class的元素 $('#id') //取得id为id的元素 //以及其余css中的选择符 2. 属性选择符 $('img[alt]') //选择带有alt属性的全部图像元素 //^表示值在字符串的开始, $表示值在字符串的结尾. *表示要匹配的值能够出如今字符串的任意位置, !表示对值取反 $('a[href^="mailto:"]') //选择页面中全部mailto:连接 $('a[href$=".pdf"]') //选择页面中全部pdf文档连接 $('a[href^="http"][href*="henry"]') //选择href属性以http开头且在任意位置包含henry的元素 3. 自定义选择符 $('div.horizontal:eq(1)') //取得集合中的第二个元素 $('tr:even') //选择奇数行. 之因此是奇数行是由于第一行的编号是0 还能够写做: $('tr').filter(':even')
$('tr:nth-child(odd)') //选择奇数行. :nth-child()是jq中惟一从1开始计数的选择符 $('tr:contains(Henry)') //根据上下文内容选择元素. contains选择符区分大小写 4. 基于表单的选择符 $('input[type="radio"]:checked') //能够选择全部选中的单选按钮 $('input[type="text"]:disabled') //选择禁用的文本输入字段
更多的基于表单的选择符
:input
:button
:enabled
:disabled
:checked:selected
filter方法
$('tr').filter(':even') $('a').filter(function(){ return this.hostname && this.hostname!=location.hostname; }) //选择包含带有域名的href属性的,且域名不等于页面当前所在域的名称的a元素
.next() //选择下一个最接近的同辈元素 $('td:contains(Henry)').next() .nextAll() //选择后面的所有同辈元素 //对应方法 .prev() .prevAll() .siblings() //选择处于相同DOM层次的全部其余元素,不管这些元素处于当前元素以前仍是以后 .parent().children() //另外一种方法
连缀方法的原理:
几乎全部的jQuery方法都会返回一个jQuery对象, 于是可连缀调用多个jQuery方法.
.get() var myTag = $('#my-element').get(0).tagName; //获取带有id为my-element属性的元素的标签名 var myTag = $('my-element')[0].tagName; //一种简写方式
函数 | 描述 |
---|---|
.add() | 将元素添加到匹配元素的集合中。 |
.andSelf() | 把堆栈中以前的元素集添加到当前集合中。 |
.children() | 得到匹配元素集合中每一个元素的全部子元素。 |
.closest() | 从元素自己开始,逐级向上级元素匹配,并返回最早匹配的祖先元素。 |
.contents() | 得到匹配元素集合中每一个元素的子元素,包括文本和注释节点。 |
.each() | 对 jQuery 对象进行迭代,为每一个匹配元素执行函数。 |
.end() | 结束当前链中最近的一次筛选操做,并将匹配元素集合返回到前一次的状态。 |
.eq() | 将匹配元素集合缩减为位于指定索引的新元素。 |
.filter() | 将匹配元素集合缩减为匹配选择器或匹配函数返回值的新元素。 |
.find() | 得到当前匹配元素集合中每一个元素的后代,由选择器进行筛选。 |
.first() | 将匹配元素集合缩减为集合中的第一个元素。 |
.has() | 将匹配元素集合缩减为包含特定元素的后代的集合。 |
.is() | 根据选择器检查当前匹配元素集合,若是存在至少一个匹配元素,则返回 true。 |
.last() | 将匹配元素集合缩减为集合中的最后一个元素。 |
.map() | 把当前匹配集合中的每一个元素传递给函数,产生包含返回值的新 jQuery 对象。 |
.next() | 得到匹配元素集合中每一个元素紧邻的同辈元素。 |
.nextAll() | 得到匹配元素集合中每一个元素以后的全部同辈元素,由选择器进行筛选(可选)。 |
.nextUntil() | 得到每一个元素以后全部的同辈元素,直到遇到匹配选择器的元素为止。 |
.not() | 从匹配元素集合中删除元素。 |
.offsetParent() | 得到用于定位的第一个父元素。 |
.parent() | 得到当前匹配元素集合中每一个元素的父元素,由选择器筛选(可选)。 |
.parents() | 得到当前匹配元素集合中每一个元素的祖先元素,由选择器筛选(可选)。 |
.parentsUntil() | 得到当前匹配元素集合中每一个元素的祖先元素,直到遇到匹配选择器的元素为止。 |
.prev() | 得到匹配元素集合中每一个元素紧邻的前一个同辈元素,由选择器筛选(可选)。 |
.prevAll() | 得到匹配元素集合中每一个元素以前的全部同辈元素,由选择器进行筛选(可选)。 |
.prevUntil() | 得到每一个元素以前全部的同辈元素,直到遇到匹配选择器的元素为止。 |
.siblings() | 得到匹配元素集合中全部元素的同辈元素,由选择器筛选(可选)。 |
.slice() | 将匹配元素集合缩减为指定范围的子集。 |
参考: jQuery 参考手册 - 遍历
$(document).ready()可简写为: $(); 即:
$(function(){ //code here });
为了不$的冲突,能够设置出让$标示符控制权
jQuery.noConflict(); //之和就经过jQuery来调用jQuery的方法
这种状况下,能够在.ready()函数中使用$的技巧----传递给ready函数的回调函数能够接收一个参数----jQuery对象自己,利用这个参数,能够从新命名jQuery为$,而没必要担忧形成冲突
jQuery(function($){ //使用$的代码 });
//.on绑定事件 $(this).on('click',function(){}); //.click $(this).click(function(){}) //添加类 $('btn').addClass('classname'); //移除类 $('btn').removeClass('classname'); //根据相应的类是否存在而添加或删除类 $('btn').toggleClass('classname');
.hover()方法: 接受两个函数参数,第一个会在鼠标指针进入被选择的元素时执行,而第二个函数会在鼠标指针离开该元素时触法
//设定事件目标 $('#switcher').click(function(event){ if(event.target = this){ $('#switcher button').toggleClass('hidden'); } }); //中止事件传播 event.stopPropagation(); //避免其余DOM元素响应事件 //阻止默认操做 .preventDefault();
同时调用.stopPropagation()和preventDefault()的一种简写方式: 在事件处理程序中返回false
$(function(){ $('#switcher').click(function(event){ if($(event.target).is('button')){ //.is()方法接收一个选择符表达式,而后用选择符来测试当前jQuery对象. 若是集合中至少有一个元素与选择符匹配,.is()返回true var bodyClass = event.target.id.split('-')[1]; $('body').removeClass().addClass(bodyClass); $('#switcher button').removeClass('selected'); $(event.target).addClass('selected'); //这里this引用的都是"#switch"对象,所以要访问被单击的按钮斗殴要经过event.target来引用 event.stopPropagation(); } }); });
.on()方法也能够接收相应参数实现事件委托. 若是给.on()方法传入的第二个参数是一个选择符表达式,jQuery会把click事件处理程序绑定到#switch对象,同时比较event.target和选择符表达式. 若是匹配, jQuery会把this关键字映射到匹配的元素, 不然不会执行事件处理程序.
.off('click')
还能够为事件处理程序添加命名空间
$('#switcher').on('click.collapse', function(){}); $('#switcher').off('click.collapse');
对于事件处理系统而言, 后缀.collapse是不可见的.
若是不使用匿名函数,则就能够不使用事件命名空间, .off()会把命名函数做为第二个参数, 结果只会解除对特定处理程序的棒的.
为了获取某个样式属性的值, 能够为这个方法传递一个字符串形式的属性名, 而后一样获得一个字符串形式的属性值. 要取得多个样式属性的值, 能够传入属性名的数组, 获得的则是属性和值勾搭的对象.
对于由多个单词构成的属性名, jQuery既能够解释连字符版的css表示法(background-color), 也能够解释驼峰大小写形式的DOM表示法(backgroundColor)
对于带浏览器前缀的属性值(好比-webkit-property-name, -ms-property-name), 用jQuery不须要提早检测, 而能够直接使用标准的属性名. 好比:
.css('propertyName','value'). //若是样式对象中不存在这个属性,jQuery就会依次检测全部带前缀(Webkit, o, Moz, ms)的属性, 而后使用第一个找到的那个属性.
.hide() .show()
能够为hide()和show()方法中指定时长参数, 产生动画效果.好比
.hide('duration')方法会同时减小元素的高度, 宽度和不透明度, 直到三个属性的值都为0. 与此同时会为该元素应用display:none. 而show('duration')方法相反.
此外,还能够指定两种预设的速度参数 'slow' 和 'fast' ,分别在600ms和200ms内完成效果. 若是传入的是其余字符串,jQuery就会在默认的400ms内完成效果.要指定更精确的速度,可使用毫秒数值. 数值不须要用引号.
一些其余显示/隐藏元素的函数:
//参数: 1. speed(毫秒/'slow'/'normal'/'fast') // 2. callback[执行完的回调参数] //淡入淡出 只是改变不可见性 .fadeIn(); //逐渐增大不透明度, 淡入 .fadeOut(); //逐渐减小不透明度, 淡出 //滑上和滑下 .slideDown() .slideUp() //切换可见性 .slideToggle();
.animate({property1: 'value1', property2: 'value2'}, duration, easing, function(){ //... });
.animate({ property1:'value1', property2:'value2' }, { duration:'value', easing: 'value', specialEasing: { property1: 'easing1', property2: 'easing2' }, complete: function(){ //... }, queue: true, step: callback });
当使用 animate() 时,必须使用 Camel 标记法书写全部的属性名,好比,必须使用 paddingLeft 而不是 padding-left,使用 marginRight 而不是 margin-right,等等。
还能够用.animate()为同一组元素应用多重效果, 能够经过连缀这些效果来实现排队
//操做非类属性 .attr(); //和.css()方法相似, 能够接收一对属性名/属性值参数 或者是一个包含键值对的对象 .removeAttr(); //还可使用值回调为每一个元素设置不一样的属性值 $(document).ready(function() { // Use attr() to add an id, rel, and title. $('div.chapter a[href*="wikipedia"]').attr({ rel: 'external', title: function(){
return 'Learn more about ' + $(this).text() //利用值回调的上下文
+ ' at Wikipedia.';
}, id: function(index, oldValue) { //值回调 return 'wikilink-' + index; } }); });
每次触发值回调, 都会给它传入两个参数. 第一个参数是整数, 表示迭代次数. 第二个参数保存的是修改以前的属性的值, 这里并无用到.
HTML属性和DOM属性:
在jQuery中, 能够经过.prop()方法取得和设置DOM属性:
//取得"checked"属性的当前值 var currentChecked = $('.my-checkbox').prop('checked'); //设置"checked"属性的值 $('.my-checkbox').prop('checked', false);
HTML属性和DOM属性差异最大就数表单控件的值. 好比,文本输入框的value属性在DOM中的属性叫defaultValue, DOM中就没有value值. 而选项列表(select)元素, 其选项的值在DOM中一般是经过selectedIndex属性, 或者经过其选项元素的selected属性来取得.
因为存在这些差别, 在取得和设置表达控件值时,最好不要用.attr()方法. 而对于选项列表, 最好也不要用.prop()方法. 建议用jQuery提供的val()方法:
//取得文本输入框的当前值 var inputValue = $('#my-input').val(); //取得选项列表的当前值 var selectValue = $('#my-select').val(); //设置单选列表的值 $('#my-single-select').val('value3'); //设置多选列表的值 $('#my-multi-select').val(['value1','value2']);
// Add "back to top" links. $('<a href="#top">back to top</a>').insertAfter('div.chapter p'); $('<a id="top"></a>').prependTo('body');
// Create footnotes. $('span.footnote').insertBefore('#footer');
// Create footnotes. $('span.footnote') .insertBefore('#footer') .wrapAll('<ol id="notes"></ol>') //把全部脚本都包含在一个<ol>中 .wrap('<li></li>') //把每个脚注分布包装在本身的<li>中
用.each()方法做为显式迭代器, 为提取脚注的位置加标记和编码
$('<p>Hello</p>').appendTo('#container'); //与下面的代码结果同样 $('#container').append('<p>Hello</p>');
因此上面的代码还能够写做:
// Create footnotes. var $notes = $('<ol id="notes"></ol>').insertBefore('#footer'); $('span.footnote').each(function(index) { $(this) .before('<sup>' + (index + 1) + '</sup>') .appendTo($notes) .wrap('<li></li>'); });
$('div.chapter p:eq(0').clone().insertBefore('div.chapter');
//要在html中建立新元素, 使用$()标签 //要在每一个匹配的元素中插入新元素: .append() .appendTo() .prepend() .prependTo() //要在每一个匹配的元素相邻的位置上插入新元素 .after() .insertAfter() .before() .insertBefore() //要在每一个匹配的元素外部插入新元素 .wrap() .wrapAll() .wrapInner() //要用新元素或文本替换每一个匹配的元素 .html() .text() .replaceAll() .replaceWith() //要移除每一个匹配的元素中的元素 .empty() //要从文档中移除每一个匹配的元素及其后代元素, 但不实际删除它们 .remove() .detach()
Ajax实质: 就是一种无需刷新页面便可从服务器(或客户端)上加载数据的手段
$(document).ready(function() { $('#letter-a a').click(function(event) { event.preventDefault(); $.ajaxSetup({ //$.ajaxSetup()函数能够修改调用Ajax方法时每一个选项的默认值. //这个函数与$.ajax()接受相同的选项对象参数. 以后全部Ajax请求都使用传递给该函数的选项, 除非明确覆盖 url: 'a.html', type: 'POST', dataType: 'html' }); $.ajax({ type: 'GET', success: function(data) { $('#dictionary').html(data); } }); }); $('#letter-b a').click(function(event) { event.preventDefault(); $.getJSON('b.json', function(data) { //$.getJSON()方法会在取得相应文件后对文件进行处理. 在数据从服务器返回后, 它只是一个简单的JSON格式的文本字符串. //$.getJSON()方法会解析这个字符串, 并将处理获得的JavaScript对象提供给调用代码 //接受两个参数, 第2个参数是当加载完成时调用的函数 //$.getJSON()函数: 没有该方法适用的DOM元素;做为结果的对象只能提供给脚本,而不能插入到页面中. //getJSON()是做为全局jQuery对象(由jQuery库定义的jQuery或$对象)的方法定义的, 而不是个别jQuery对象实例的方法 //可把getJSON()方法看作类方法, 称其为全局函数. 这些全局函数使用的是jQuery命名空间. var html = ''; $.each(data, function(entryIndex, entry) { //$.each()函数不操做jQuery对象, 它以数组或对象做为第一个参数,以回调函数做为第二个参数. //此外还须要将每次循环中数组或对象的当前索引和当前项做为回调函数的两个参数 html += '<div class="entry">'; html += '<h3 class="term">' + entry.term + '</h3>'; html += '<div class="part">' + entry.part + '</div>'; html += '<div class="definition">'; html += entry.definition; if (entry.quote) { html += '<div class="quote">'; $.each(entry.quote, function(lineIndex, line) { html += '<div class="quote-line">' + line + '</div>'; }); if (entry.author) { html += '<div class="quote-author">' + entry.author + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; }); $('#dictionary').html(html); }); }); $('#letter-c a').click(function(event) { event.preventDefault(); $.getScript('c.js'); //$.getScript()也是一个全局函数. 接受一个url参数以查找脚本文件. //以这种方式取得的脚本会在全局环境下执行. 这意味着脚本有权访问在全局环境中定义的函数和变量, 固然也包括jQuery自身 }); $('#letter-d a').click(function(event) { event.preventDefault(); $.get('d.xml', function(data) { //加载xml文档. //一般,$.get()函数只是取得由url指定的文件, 而后将纯文本格式的数据提供给回调函数. 可是在根据服务器提供的mine类型知道相应的是xml的状况下, 提供给回调函数的将是xml dom树 $('#dictionary').empty(); $(data).find('entry').each(function() { var $entry = $(this); var html = '<div class="entry">'; html += '<h3 class="term">' + $entry.attr('term'); html += '</h3>'; html += '<div class="part">' + $entry.attr('part'); html += '</div>'; html += '<div class="definition">'; html += $entry.find('definition').text(); //jQuery内部的选择符引擎, 对查找xml文档元素也有效 var $quote = $entry.find('quote'); if ($quote.length) { html += '<div class="quote">'; $quote.find('line').each(function() { html += '<div class="quote-line">'; html += $(this).text() + '</div>'; }); if ($quote.attr('author')) { html += '<div class="quote-author">'; html += $quote.attr('author') + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; $('#dictionary').append($(html)); }); }); }); $('#letter-e a').click(function(event) { event.preventDefault(); //为了防止单击这些连接时打开新的url. //当默认动做是从新加载页面或从新打开新页面时, 推荐使用preventDefault()而不是return false结束该处理程序. //return false意味着同时调用event.preventDefault()和event.stopPropagation(), 所以想要阻止事件冒泡, 还得调用后者 var requestData = {term: $(this).text()}; $.get('e.php', requestData, function(data) { //向服务器传递数据. 第二个参数是一个用来构建查询关键字符串的键和值的对象. $('#dictionary').html(data); }).fail(function(jqXHR) { $('#dictionary') .html('Sorry, but an error occurred: ' + jqXHR.status) .append(jqXHR.responseText); }); }); $('#letter-f form').submit(function(event) { event.preventDefault(); var formValues = $(this).serialize(); $.get('f.php', formValues, function(data) { $('#dictionary').html(data); }); }); var url = 'http://examples.learningjquery.com/jsonp/g.php'; $('#letter-g a').click(function(event) { event.preventDefault(); $.getJSON(url + '?callback=?', function(data) { //使用JSONP加载远程数据, 实现跨域
//能够经过使用 JSONP 形式的回调函数来加载其余网域的 JSON 数据,如 "url?callback=?"。jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。 注意:此行之后的代码将在这个回调函数执行前执行。
//该函数是简写的Ajax函数. 等价于: $.ajax({url:url, data: data, success: callback, dataType: json});
var html = ''; $.each(data, function(entryIndex, entry) { html += '<div class="entry">'; html += '<h3 class="term">' + entry.term + '</h3>'; html += '<div class="part">' + entry.part + '</div>'; html += '<div class="definition">'; html += entry.definition; if (entry.quote) { html += '<div class="quote">'; $.each(entry.quote, function(lineIndex, line) { html += '<div class="quote-line">' + line + '</div>'; }); if (entry.author) { html += '<div class="quote-author">' + entry.author + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; }); $('#dictionary').html(html); }); }); $('#letter-h a').click(function(event) { event.preventDefault(); $('#dictionary').load('h.html .entry'); }); var $loading = $('<div id="loading">Loading...</div>') .insertBefore('#dictionary'); //jQuery提供的一组观察员函数, 来为各类与Ajax相关的事件注册回调函数. //这些观察员都是全局性的,且只能由$(document)调用 $(document).ajaxStart(function() { //当Ajax请求开始且还没有进行其余传输时出发.ajaxStart()的回调函数 $loading.show(); }).ajaxStop(function() { //最后一次活动请求终止时出发 $loading.hide(); }); //全局观察员函数还有.ajaxError() $('body').on('click', 'h3.term', function() { $(this).siblings('.definition').slideToggle(); //事件委托 }); });
为了防止jQuery的别名$已经被让渡出去, 能够在插件的做用域内定义这个快捷方式, 使用当即调用的函数表达式(IIFE, Immediately Invoked Function Expression)
(function($){ //code here })(jQuery);
所谓全局函数, 实际上就是jQuery对象的方法, 但从实践的角度上看,它们是位于jQuery命名空间内部的函数. 好比$.ajax(), $.each(), $.map().
给jQuery添加新的全局方法就是只要在第一节中的IIFE内部定义一个方法, 在函数外部就能够调用了. 由于它已是jQuery的对象方法了. 好比:
/****************************************************************************** Our plugin code comes first in this document. Normally, plugins would appear in separate files named jquery.plugin-name.js, but for our examples it's convenient to place this plugin code in the same JavaScript file as the code that calls it. ******************************************************************************/ /****************************************************************************** $.sum() Return the total of the numeric values in an array/object. ******************************************************************************/ (function($) { $.sum = function(array) { var total = 0; $.each(array, function(index, value) { value = $.trim(value); value = parseFloat(value) || 0; total += value; }); return total; }; $.average = function(array) { if ($.isArray(array)) { return $.sum(array) / array.length; } return ''; }; })(jQuery); /****************************************************************************** End plugin code; begin custom script code. ******************************************************************************/ $(document).ready(function() { var $inventory = $('#inventory tbody'); var quantities = $inventory.find('td:nth-child(2)') .map(function(index, qty) { return $(qty).text(); }).get(); var sum = $.sum(quantities); $('#sum').find('td:nth-child(2)').text(sum); });
此外, 还能够利用$.extend()函数经过另外一种语法定义全局函数:
(function($) { $.extend({ sum: function(array) { var total = 0; $.each(array, function(index, value) { value = $.trim(value); value = parseFloat(value) || 0; total += value; }); return total; }, average: function(array) { if ($.isArray(array)) { return $.sum(array) / array.length; } return ''; } }); })(jQuery);
为了不冲突, 能够把属于一个插件的全局对象都封装到一个对象中. 即便用命名空间隔离函数 .好比:
(function($) { $.mathUtils = { sum: function(array) { var total = 0; $.each(array, function(index, value) { value = $.trim(value); value = parseFloat(value) || 0; total += value; }); return total; }, average: function(array) { if ($.isArray(array)) { return $.mathUtils.sum(array) / array.length; } return ''; } }; })(jQuery);
调用时须要:
var sum = $.mathUtils.sum(quantities); var average = $.mathUtils.average(prices);
(function($) { $.fn.swapClass = function(class1, class2) { this.each(function() { var $element = $(this); if ($element.hasClass(class1)) { $element.removeClass(class1).addClass(class2); } else if ($element.hasClass(class2)) { $element.removeClass(class2).addClass(class1); } }); }; })(jQuery);
(function($) { $.fn.swapClass = function(class1, class2) { return this.each(function() { var $element = $(this); if ($element.hasClass(class1)) { $element.removeClass(class1).addClass(class2); } else if ($element.hasClass(class2)) { $element.removeClass(class2).addClass(class1); } }); }; })(jQuery);
这样, 就能够在插件方法上连缀内置方法了
所谓内部函数, 就是定义在另外一个函数中的函数. 能够避免污染命名空间.
而JS中的内部函数能够逃脱定义它们的外部函数. 逃脱的方法有多种:
var globalVar; function outerFn() { console.log('Outer function'); function innerFn() { console.log('Inner function'); } globalVar = innerFn; } console.log('outerFn():'); outerFn(); console.log('globalVar():'); globalVar();
function outerFn() { console.log('Outer function'); function innerFn() { console.log('Inner function'); } return innerFn; } console.log('var fnRef = outerFn():'); var fnRef = outerFn(); //Outer function console.log('fnRef():'); fnRef();//Inner function
当内部函数在定义它的做用域的外部被引出时, 就建立了该内部函数的一个闭包.
这种状况下, 咱们称既不是内部函数局部变量, 也不是其参数的变量为自由变量, 称外部函数的调用环境为封闭闭包的环境. 从本质上将, 若是内部函数引用了位于外部函数中的变量, 至关于受权该变量可以被延迟使用. 所以, 当外部函数引用完成后, 这些变量的内存不会被释放, 由于闭包仍然须要使用它们.
依然要注意如下这个问题:
$(document).ready(function($) { // Stuff to do as soon as the DOM is ready; for(var i=0; i<5; i++){ $('<div>Print ' + i + '</div>') .click(function(){ console.log(i); }).insertBefore('body'); } });
这里单击其中一项并不会看到相应的编号输出, 而是都会显示数值5. 即即便在绑定程序时i的值每次都不同, 每一个click处理程序最终引用的i都相同. 都等于单击事件实际发生时i的最终值(5).
要解决这个问题, 方法有:
$(document).ready(function() { // Stuff to do as soon as the DOM is ready; $.each([0,1,2,3,4], function(index, value) { $('<div>Print ' + value + '</div>') .click(function(){ console.log(value); }).insertBefore('body'); }); });
这是由于函数的参数相似于在函数中定义的变量, 因此每次循环的value值实际上都是不一样的变量. 结果, 每一个click处理程序都指向一个不一样的value变量, 于是每次单击输出的值会与元素的标签文本匹配.
$(document).ready(function() { for(var i=0; i<5; i++){ (function(value){ $('<div>Print '+ value + '</div>') .click(function(){ console.log(value); }).insertBefore('body'); })(i); } });
$(document).ready(function() { for(var i=0; i<5; i++){ $('<div>Print '+ i + '</div>') .on('click', {value: i}, function(event){ console.log(event.data.value); }).insertBefore('body'); } });
由于event是函数的参数, 每次调用处理程序时它都是一个独立的实例, 而不是在全部调用中共享的一个值.
闭包可能会致使在不经意间建立引用u型拟合. 好比:
function outerFn() { var outerVar = {}; function innerFn() { console.log(outerVar); } outerVar.fn = innerFn; return innerFn; };
这里innerFn()建立了一个引用outerVar的闭包, 而outerVar又引用了innerFn()
而更隐蔽的情形是:
function outerFn() { var outerVar = {}; function innerFn() { console.log('hello'); } outerVar.fn = innerFn; return innerFn; };
这里虽然innerFn()没有引用outerVar. 可是仍然没有断开循环. 即便innerFn()再也不引用outerVar, outerVar也仍然位于innerFn()的封闭环境中. 因为闭包的缘由, 位于outerFn()中的全部变量都隐含地被innerFn()所引用. 所以, 闭包会使意外地建立这些引用循环变得容易.
以上这种状况一般容易处理, 由于js可以检测到这些状况并在它们孤立时将其清除.
旧版本IE中存在的一种难以处理的引用循环问题. 当一个循环中同时包含DOM元素和常规JS元素时, IE没法释听任何一个对象----由于这两类对象是由不一样的内存管理程序负责管理的. 即除非关闭浏览器, 不然这种循环在IE中永远得不到释放. 好比:
$(document).ready(function() { var button = document.getElementById('button-1'); button.onclick = function() { console.log('hello'); return false; }; });
当指定单击事件处理程序时, 就建立了一个在其封闭的环境中包含button变量的闭包. 并且, 如今的button也包含一个指向闭包(onclick属性自身)的引用. 这样, 就致使了在IE中即便离开当前页面也不会释放这个循环.
为了释放内存, 就须要断开循环引用, 例如在关闭窗口关删除onclick属性. 另外, 能够向以下重写代码来避免:
function hello() { console.log('hello'); return false; } $(document).ready(function() { var button = document.getElementById('button-1'); button.onclick = hello; });
这样,由于hello函数再也不包含button, 引用就成了单向的(从button到hello), 不存在的循环, 就不会形成内存泄露了.
$(document).ready(function() { var $button = $('#button-1'); $button.click(function(event) { event.preventDefault(); console.log('hello'); }); });
即便此时仍然会建立一个闭包, 而且也会致使同前面同样的循环, 但这里的代码却不会使IE发生内存泄露. 因为jQuery考虑到了内存泄露的潜在危害, 因此它会手动释放本身指定的全部事件处理程序.
另外一种避免泄露的工具-----使用.data()方法能够像使用扩展属性同样, 将信息附加到DOM元素. 因为这里的数据并不是直接保存在扩展属性中, 所以永远也不会构成引用循环.