我们需要理解jQuery背后的工作原理,掌握现代浏览器API,把Web开发从jQuery的陷阱中拯救出来,jQuery不等于JavaScript!
原文:http://blog.garstasio.com/you-dont-need-jquery/
抛弃jQuery:Why?
现在的Web工程师太依赖jQuery了,某种意义上说jQuery已经成了JavaScript的同义词。但是我们真的需要他么?或许我们应该反思一下什么时候才真的需要jQuery。
对我个人而言开始使用jQuery的理由是他把我的工作变得简单多了,开发Web应用已经几乎离不开它。曾经在不同浏览器里Web API的实现有很大区别,而jQuery帮我抹平了这些,所以我很少再用document.getElementById这样的原生JavaScript 函数。依赖jQuery衍生出了无数极其优秀的类库,从完美的下拉菜单、复杂的表单验证到这几年流行的瀑布流布局,这些都让我的工作变得简单多了。
我一直坚信jQuery是开发JavaScript工程必须的,2012年,当时我需要开发维护一个跨浏览器的大文件上传组件时,我的第一直觉就是我要用jQuery重写已有的代码, 因为我已经下意识把jQuery当做简化工作的一部分标准。但是社区的用户并不希望我这样做——他们不想引入额外的第三方类库,就这样我(被迫/不情愿) 的重新开始学习原生浏览器API。出于意料的是,我发现不再依赖jQuery之后的工作比我想象的要简单得多!我曾经以为没有jQuery我就不会写 JavaScript代码了,但是现在我发现jQuery并不是必须的!
拐杖?陷阱?
曾几何时我第一次使用JavaScript开发大型项目,jQuery就同步进入了我的工作,事实上我是从见识了无比强大的jQuery选择符系统 之后才开始喜欢上Web开发的。我并没有深入学过“真正的”浏览器原生API(document.getElementById ?那时觉得看着好丑!),在被迫学习原生API之前,事实上我也不完全清楚怎么直接访问并操作DOM元素——jQuery全都帮我做了。jQuery已经 成了我的拐杖,当初用它是因为他能让我走的更好,但是后来我离开他已经不会走路了。
我发现我掉进了一个陷阱,一个很多Web开发新手都掉进的陷阱。我本应该先花时间去理解JavaScript本身以及浏览器提供的API,但是我却因为jQuery提供的蜜罐而止步不前。逻辑上我们应该这样系统的学习JavaScript开发:
- 学习JavaScript语言
- 学习浏览器API
- 学习jQuery(或者其他框架、类库,实际项目中会大量用到的)
译者注:事实上这也是JavaScript的圣经犀牛书(JavaScript: The Definitive Guide)的撰写顺序。可实际上包括译者在内,很多Web开发新手看到"第二部分:客户端Javascript"的时候就直接略过了,毕竟那时觉得和浏览器API比起来,jQuery看上去是那么优雅。
工程实践中,很多Web开发新手(比如我)是从第3步开始的,完全忽略了1和2的存在,这是完全可以理解的,因为学会了jQuery(或者其他类 库)我们就已经可以动手开始写代码了~ 但是如果你不清楚jQuery的背后到底发生了什么,就一定会在未来的开发中遇到问题。你也一定会遇到不能使用jQuery的项目,比如流行的 Angular.js这样的框架,初学者就最好删掉jQuery类库,从头开始学JavaScript。
跨浏览器支持
支持jQuery最常见的理由中最常见的一条,就是他修复了“不一致的DOM API”。这没错,但事实上不一致的DOM API只有在IE6/7及更早的版本中才大量存在。浏览器发展到2014年,非现代浏览器的使用比例已经越来越少。jQuery开发组自身也意识到这个问题,逐渐开始削减对这部分浏览器的支持,从jQuery 3.0开始,jQuery的版本分化为支持所有浏览器的完整版和只支持现代浏览器的精简版。我们需要面对的DOM API已经没有那么糟糕,基本的DOM元素创建、遍历和操作已经统一,至少在所有的现代浏览器中是这样。
从IE8开始,浏览器API开始逐渐标准、稳定,当然从细节上,IE10及早期的Safari/WebKit引擎中的某些实现确实不完全相同,但是 这些不同可以逐例分析,并且使用更小、更专用的类库来抹平。重点是:jQuery不是银弹,不能解决脚本中的所有跨浏览器问题,我们可以用更小、更专用且 可控的方式来抹平这些浏览器的差异。
JavaScript
另一个支持jQuery的常见理由是它弥补了一些JavaScript自身的缺陷,比如不方便的循环遍历、复杂的DOM访问等等。使用了第三方类库 (jQuery或Underscore等)之后,循环遍历变得比原来容易多了。这个理由曾经是正确的,但是现在JavaScript本身也在进 化,forEach、Object.keys()等函数的支持也很普遍了。曾经我很依赖$.inArray()函数,但是Array.prototype.indexOf()函数也早就是ECMA Script 5的一部分。类似的例子还有很多,后面我们可以在专题中慢慢探索。
需要马上抛弃jQuery么?
当然不是,如果jQuery使你的工作变得足够简单,如果你足够熟悉jQuery是如何工作的,接着使用jQuery没有一点儿问题。这个系列的文 章是想告诉读者,我们可以使用原生的浏览器API完成需要的工作,而不需要引入庞大且不完全需要的类库。另外多了解jQuery本身是如何工作的总是没有 坏处。
译者注:
The better you understand what you are doing, the better you will do it.
对所做的事情理解越深,你就会做的越好。
——引自 The Singular Secret of the Rockstar Programmer / 编程巨星的唯一秘诀
抛弃jQuery:DOM API 之选择元素
jQuery的选择符模块无比优雅,以至于我见过很多Web框架和应用中引用了庞大的jQuery,只是因为它提供了方便的DOM元素选择函数。我 已经数不清自己写过多少次 $(#myElement) 或者 $(‘.myElement‘) 了,以至于在没有jQuery时经常束手无策。事实上使用DOM API选择元素并没有那么难,它或许没有jQuery的那么简短,不过用起来也足够简单了。
- ID
- CSS Classes
- HTML标签名
- HTML属性
- 伪类
- 子元素
- 后代元素
- 排除元素
- 多重选择
- 仿造jQuery的“$”
- 专用的选择符模块
ID
jQuery
// 返回一个jQuery对象
$(‘#myElement‘);
DOM API,我们最常见到的是这样:
// IE 5.5+
document.getElementById(‘myElement‘);
IE 8及以上版本的浏览器中可以使用querySelector函数:
// IE 8+
document.querySelector(‘#myElement‘);
这两种DOM API函数都直接返回一个元素,有测试表明getElementById函数比querySelector函数效率更高一些。
随着浏览器升级,对querySelector函数的支持越来越好,jQuery的选择函数还有什么决定性的优势么?
CSS Classes
jQuery
// 返回所有匹配元素的jQuery对象
$(‘.myElement‘);
DOM API,IE 9及以上版本的浏览器中有专用的getElementsByClassName函数:
// IE 9+
document.getElementsByClassName(‘myElement‘);
IE 8及以上版本的浏览器中可以使用querySelectorAll函数:
// IE 8+
document.querySelectorAll(‘.myElement‘);
两种DOM API中getElementsByClassName的效率最高,返回一个HTMLCollection集合。后一种(querySelectorAll)返回NodeList类型。
jQuery能做到的,DOM API同样也做到了,不是么?
HTML 标签名
假设我们要选择所有的 div 元素:
jQuery
$(‘div‘);
DOM API,最常见的函数是这个:
// IE 5.5+
document.getElementsByTagName(‘div‘);
IE 8及以上版本的浏览器中依然可以使用querySelectorAll函数:
// IE 8+
document.querySelectorAll(‘div‘);
两种DOM API相比,getElementsByTagName的效率会稍微高一些。
HTML 属性
假设我们要选择 data-foo-bar 属性为 someval 的元素:
jQuery
$(‘[data-foo-bar="someval"]‘);
DOM API,IE 8及以上版本的浏览器中可以继续使用万能的querySelectorAll函数:
// IE 8+
document.querySelectorAll(‘[data-foo-bar="someval"]‘);
伪类
假设我们要从 id=myForm 的 from 元素中选择具备 :invalid 伪类的元素:
jQuery
$(‘#myForm :invalid‘);
DOM API,IE 8及以上版本的浏览器中可以继续使用万能的querySelectorAll函数:
// IE 8+
document.querySelectorAll(‘#myForm :invalid‘);
子元素
假设父元素 id="myParent" ,如果我们只是想简单的选择所有子元素:
jQuery
$(‘#myParent‘).children();
DOM API,最熟悉的是这个:
// IE 5.5+
// NOTE: This will include comment and text nodes as well.
document.getElementById(‘myParent‘).childNodes;
IE 9及以上版本的浏览器中可以直接使用children来获取:
// IE 9+
// NOTE: This ignores comment & text nodes.
document.getElementById(‘myParent‘).children;
如果只是想获取包含 ng-click 属性的直接子元素呢?
jQuery
$(‘#myParent‘).children(‘[ng-click]‘);
// 或者
$(‘#myParent > [ng-click]‘);
DOM API,我是从这时开始发现querySelector比我想象的要强大…
// IE 8+
document.querySelector(‘#myParent > [ng-click]‘);
后代元素
假设祖先节点 id="myParent",我们希望获取其后代的所有超链接:
jQuery
$(‘#myParent A‘);
DOM API,IE 8及以上版本的浏览器里可以这样:
// IE 8+
document.querySelectorAll(‘#myParent A‘);
排除元素
假设我们要从 div 元素中获取出不带“ignore” class的元素
jQuery
$(‘DIV‘).not(‘.ignore‘);
// 或者
$(‘DIV:not(.ignore)‘);
DOM API,IE 8及以上版本的浏览器中可以这样:
// IE 9+
document.querySelectorAll(‘DIV:not(.ignore)‘);
多重选择
假设我们要选择所有的 div , a 和 script 元素:
jQuery
$(‘DIV, A, SCRIPT‘);
DOM API,IE 8及以上版本的浏览器里可以这样:
// IE 8+
document.querySelectorAll(‘DIV, A, SCRIPT‘);
仿造 jQuery 的 “$”
发现什么规律了么?
如果我们只考虑IE8及以上的浏览器,我们可以通过简单的代码“仿造”出类似jQuery中“$”选择符的效果:
window.$ = function(selector) {
var selectorType = ‘querySelectorAll‘;
if (selector.indexOf(‘#‘) === 0) {
selectorType = ‘getElementById‘;
selector = selector.substr(1, selector.length);
}
return document[selectorType](selector);
};
这段代码之后,你就可以在脚本中使用$来进行大部分选择元素的操作了。
可以替代 jQuery 的专用选择符模块
对于大部分JavaScript项目来说,原生的浏览器API已经足够进行DOM元素的选择了,但是我们也注意到,这些函数在低版本的IE浏览器中不能很好的工作。为了兼容低版本的浏览器,我们需要引入一些第三方的模块来帮助我们完成选择元素的任务。
当然直接引入jQuery是最直接的方法,但是我们如果我们只是为了享受选择元素的便利,那jQuery显然大材小用(浪费带宽)了。我们不妨试试Sizzle,这是一个很小的模块,专注于选择DOM元素,事实上jQuery正是使用了Sizzle作为它的一部分。Selectivizr是另一个选择,同样很小,专注于在较早版本的浏览器中支持CSS3选择符,他同样被包含在jQuery, prototype, mootools等框架中。
如果我漏掉了什么重要的选择符,请在评论里告诉我。
抛弃jQuery:DOM API 之操作元素
在上一篇文章里我们讨论了如何在没有jQuery的支持下选择元素,这次我们来聊一聊如何使用DOM API创建新元素、修改已有元素的属性或者移动元素的位置。原生的浏览器API已经给我们提供了DOM操作的所有功能,我们能够不借助jQuery或者其他函数库的帮助就完成这些。
当然有些操作会显得比较冗长,所以我必须再强调一遍,这系列文章的用意不是让开发人员不分情况的彻底丢弃jQuery,而是希望我们能够在没有jQuery的帮助时也能完成这些任务。在实际生产中我们依然会使用各种函数库,但是我们可以仔细选择,使用更加适合我们需要的库,而不是不分情况的把整个jQuery都加载到页面里。
- 创建元素
- 在元素前/后插入元素
- 作为子元素插入
- 移动元素
- 删除元素
- 添加/删除 CSS Classes
- 添加/删除/修改元素属性
- 添加/修改内容
- 添加/修改 Style
- 专用的DOM操作库
创建元素
jQuery
如果我们要创建一个 div 元素:
$(‘<div></div>‘);
DOM API,createElement这个很常见了:
// IE 5.5+
document.createElement(‘div‘);
或许有人会说,jQuery省去了好些字符。这没错,但确实要依照工程实际而定,如果我们只是用到jQuery的小部分功能,那省去的字数可能还没有jQuery自身的文件大。
在元素前/后插入元素
假设我们已经有了如下HTML结构:
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
现在我们要在 id=1 的 div 后面插入一个 id=1.1 的新 div 元素,期望的结果是这样的:
<div id="1"></div>
<div id="1.1"></div>
<div id="2"></div>
<div id="3"></div>
jQuery
$(‘#1‘).after(‘<div id="1.1"></div>‘);
DOM API,有Web开发新人会认为链式调用是jQuery的专利,事实上没有jQuery我们也可以使用链式调用:
// IE 4+
document.getElementById(‘1‘)
.insertAdjacentHTML(‘afterend‘, ‘<div id="1.1"></div>‘);
哈哈,这可能是DOM API里不多的从IE 4开始就没怎么变化过的函数了~
假设我们希望在 id=1 的 div 前面插入一个 id=0.9 的新 div 元素,期望的结果是这样:
<div id="0.9"></div>
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
jQuery
$(‘#1‘).before(‘<div id="0.9"></div>‘);
DOM API
// IE 4+
document.getElementById(‘1‘)
.insertAdjacentHTML(‘beforebegin‘, ‘<div id="0.9"></div>‘);
同在后面插入相比,只有一个参数不同。不得不说,看到一个函数被IE 4以上所有的浏览器都支持的时候,作为一个前端开发人员我还是挺惊讶的 ^_^
作为子元素插入
假设我们已经有了如下HTML结构:
<div id="parent">
<div id="oldChild"></div>
</div>
现在我们希望在 parent 容器的第一个子元素之前插入一个新的 div 元素,就象这样:
<div id="parent">
<div id="newChild"></div>
<div id="oldChild"></div>
</div>
jQuery
$(‘#parent‘).prepend(‘<div id="newChild"></div>‘);
DOM API
// IE 4+
document.getElementById(‘parent‘)
.insertAdjacentHTML(‘afterbegin‘, ‘<div id="newChild"></div>‘);
如果我们希望在第一个子元素的后面插入那个 div 元素,期望得到这样的结构:
<div id="parent">
<div id="oldChild"></div>
<div id="newChild"></div>
</div>
jQuery
$(‘#parent‘).append(‘<div id="newChild"></div>‘);
DOM API
// IE 4+
document.getElementById(‘parent‘)
.insertAdjacentHTML(‘beforeend‘, ‘<div id="newChild"></div>‘);
这一节看上去和上一节差不多,总之我们发现使用DOM API操作元素也不是很复杂,而且到目前为止对浏览器的兼容性都很不错,包括古老的IE浏览器也能很好的适应。
移动元素
假设有以下HTML结构:
<div id="parent">
<div id="c1"></div>
<div id="c2"></div>
<div id="c3"></div>
</div>
<div id="orphan"></div>
在这里希望把 #orphan 移动到 parent 容器中作为最后一个元素,期望的HTML是:
<div id="parent">
<div id="c1"></div>
<div id="c2"></div>
<div id="c3"></div>
<div id="orphan"></div>
</div>
jQuery
$(‘#parent‘).append($(‘#orphan‘));
DOM API
// IE 5.5+
document.getElementById(‘parent‘)
.appendChild(document.getElementById(‘orphan‘));
也还是蛮好记的对不对?如果我们希望把 #orphan 移动到 parent 容器的第一个元素,类似于:
<div id="parent">
<div id="orphan"></div>
<div id="c1"></div>
<div id="c2"></div>
<div id="c3"></div>
</div>
jQuery
$(‘#parent‘).prepend($(‘#orphan‘));
DOM API
// IE 5.5+
document.getElementById(‘parent‘)
.insertBefore(document.getElementById(‘orphan‘), document.getElementById(‘c1‘));
直接使用DOM API看上去确实很长,这都是拜 document.getElementById 所赐。
删除元素
假设我们的HTML元素中有一个 id="foobar" 的元素,我们希望把他从DOM树中删除:
jQuery
$(‘#foobar‘).remove();
DOM API
// IE 5.5+
document.getElementById(‘foobar‘)
.parentNode.removeChild(document.getElementById(‘foobar‘));
添加/删除 CSS Classes
假设在HTML中有这样一个 div :
<div id="foo"></div>
我们希望给他加入名为 bold 的CSS Class,期望DOM变成这样:
<div id="foo" class="bold"></div>
jQuery
$(‘#foo‘).addClass(‘bold‘);
DOM API
document.getElementById(‘foo‘).className += ‘bold‘;
当然我们还可以把刚刚加上的Class删除掉:
jQuery
$(‘#foo‘).removeClass(‘bold‘);
DOM API
// IE 5.5+
document.getElementById(‘foo‘).className =
document.getElementById(‘foo‘).className.replace(/^bold$/, ‘‘);
添加/删除/修改元素属性
仍然从简单的 foo 元素开始:
<div id="foo"></div>
我们希望在 div 元素上设定 role="button" 来让它能够充当一个 button,这样能够在可访问性设备(屏幕阅读器等)上获得较好的可访问性:
jQuery
$(‘#foo‘).attr(‘role‘, ‘button‘);
DOM API
// IE 5.5+
document.getElementById(‘foo‘).setAttribute(‘role‘, ‘button‘);
这两种方法都可以识别元素是否已经具备了这个属性,然后自动创建/更新这个属性。现在让我们再把这个 role 删除掉(我都觉得自己好烦…)
jQuery
$(‘#foo‘).removeAttr(‘role‘);
DOM API
// IE 5.5+
document.getElementById(‘foo‘).removeAttribute(‘role‘);
添加/修改内容
这次的 foo 元素比原来复杂多了(^_^):
<div id="foo">Hi there!</div>
如果要把 foo 的内容改成 Goodbye!:
jQuery
$(‘#foo‘).text(‘Goodbye!‘);
DOM API
// IE 5.5+
document.getElementById(‘foo‘).innerHTML = ‘Goodbye!‘;
// IE 5.5+ but NOT Firefox
document.getElementById(‘foo‘).innerText = ‘GoodBye!‘;
// IE 9+
document.getElementById(‘foo‘).textContent = ‘Goodbye!‘;
最常见的 innerHTML 属性在各种条件下都工作的很好。但是 innerText 和 textContent 的好处在于他们只是处理目标元素内的文字,而不是像 innerHTML 一样直接插入标签。当插入的内容是用户输入的内容时,这样能够一定程度上避免注入的问题。
添加/修改 Style
一般来说,直接用JavaScript脚本操作DOM元素的 style 是代码中的“坏味道”,当然就像 goto 一样我们总会有些时候需要这样做。
假设HTML的结构是这样的:
<span id="note">Attention!</span>
可以通过JavaScript代码让这段文字变得醒目一些,比如变成粗体(或者闪烁三下/转体三圈):
jQuery
$(‘#note‘).css(‘fontWeight‘, ‘bold‘);
DOM API
// IE 5.5+
document.getElementById(‘note‘).style.fontWeight = ‘bold‘;
这里很难得我比较喜欢DOM API写法,style.fontWeight 看上去比较像是正常的对象-属性的逻辑。
专用的DOM操作库
看到这里比上一篇选择元素要容易一些,在IE 6及以上的版本中跨浏览器使用DOM API操作元素相对比较容易。jQuery确实使得冗长的DOM API变得简单易懂,但是如果只是把jQuery用作DOM操作,似乎还是有些大材小用。我们可以试试jBone和dom.js这两个库,他们都可以在跨浏览器的情况下很好的兼容DOM元素操作,著名的Backbone中使用的正是前者。很多使用jQuery的开发人员觉得原生的DOM API不堪入目,但是我想他们并不真的掌握DOM API,其实DOM API并没有比jQuery复杂太多。
抛弃jQuery:Ajax
大部分人开发者在学习jQuery的时候,看到 $.ajax() 函数都会觉得这真是太棒了!是的,它魔法般的把 ajax 请求变得极其容易,同时提供了足够多的控制项。在浏览器API层面这些请求都是由 XMLHttpRequest 对象完成的,$.ajax() 函数对它做了包装,这篇文章里快速浏览一下如何通过浏览器API提交ajax请求,包括跨域的ajax请求,试过之后可以发现这其实也不是很复杂。
译者注:在工程中使用原生代码进行Ajax操作是不明智的,作者也并非希望手工处理浏览器差异,只是希望能够理解jQuery背后的工作原理。
GETting POSTing URL Encoding JSON Uploading CORS JSONP Libraries to Consider Next in this Series
GET 请求
我们从一个简单的请求开始,向服务器提交一个查询,通过用户的ID查询用户的名字,在URI的参数中包含了请求的用户ID。在请求结束之后弹出alert对话框提示结果。
jQuery
在jQuery中提交GET请求有两种方法,可以使用 $.get() 函数进行简单的请求,或者使用 $.ajax() 函数来获得更多的控制。在这里我们统一使用 $.ajax() 函数来统一所有的请求。
$.ajax(‘myservice/username‘, {
data: {
id: ‘some-unique-id‘
}
})
.then(
function success(name) {
alert(‘User\‘s name is ‘ + name);
},
function fail(data, status) {
alert(‘Request failed. Returned status of ‘ + status);
}
);
Native XMLHttpRequest Object
var xhr = new XMLHttpRequest();
xhr.open(‘GET‘, encodeURI(‘myservice/username?id=some-unique-id‘));
xhr.onload = function() {
if (xhr.status === 200) {
alert(‘User\‘s name is ‘ + xhr.responseText);
}
else {
alert(‘Request failed. Returned status of ‘ + xhr.status);
}
};
xhr.send();
上面这段JS代码在IE 7及以上的浏览器中都能正常的工作,即使是IE 6中也提供了有限的支持。
POST 请求
上一节通过GET请求我们已经能够通过用户ID获取用户的名字,在这里我们进一步修改他/她的名字,并为其添加“地址”字段。当然这个操作通过 GET请求也是可以完成的,但仍然推荐使用POST请求。我们需要向服务器提交POST请求,请求中包含了用户的新名字以及地址信息,服务器收到请求后会 加以处理并将更新后的名字返回,在JS里我们要验证返回的新名字与我们要修改的是否一致。
如果考虑到HTTP 1.1标准,这里我们更应该使用PATCH请求,PATCH并不是传统意义上通用的请求方式,在IE 8等较老的浏览器中存在某些问题,因此这里而我们只讨论POST请求。对于代码来说这两种请求的区别倒是不大,只有请求的方式字段不同。
jQuery
var newName = ‘John Smith‘;
$.ajax(‘myservice/username?‘ + $.param({id: ‘some-unique-id‘}), {
method: ‘POST‘,
data: {
name: newName
}
})
.then(
function success(name) {
if (name !== newName) {
alert(‘Something went wrong. Name is now ‘ + name);
}
},
function fail(data, status) {
alert(‘Request failed. Returned status of ‘ + status);
}
);
Native XMLHttpRequest Object
var newName = ‘John Smith‘,
xhr = new XMLHttpRequest();
xhr.open(‘POST‘,
encodeURI(‘myservice/username?id=some-unique-id‘));
xhr.setRequestHeader(‘Content-Type‘, ‘application/x-www-form-urlencoded‘);
xhr.onload = function() {
if (xhr.status === 200 && xhr.responseText !== newName) {
alert(‘Something went wrong. Name is now ‘ + xhr.responseText);
}
else if (xhr.status !== 200) {
alert(‘Request failed. Returned status of ‘ + xhr.status);
}
};
xhr.send(encodeURI(‘name=‘ + newName));
原生的API这样工作的也还算不错。
译者注:原生的方式写起来很复杂,而且需要手工处理 Content-Type 这样的HTTP头,手工判断返回的HTTP状态码。工程中强烈不推荐使用这样的代码,作者在这里强调原生API也能完成POST请求,只是为了说明 jQuery背后的工作原理,并非推荐在工程中直接使用这样的代码。
URL 编码
jQuery中提供了工具方法,对请求中需要传递的数据进行编码:
$.param({
key1: ‘some value‘,
‘key 2‘: ‘another value‘
});
在浏览器API中,提供了两个函数:encodeURI 和 encodeURIComponent 用于进行URL转码,两者的区别在于前者用于完整的URI的转码,因此URI中的合法字符(斜线等)不会被转码;后者用于URI的一部分(比如协议、主机名、路径或查询字符串)转码,因此所有可以转码的内容都会被转码。
如果我们想要“仿造”一个jQuery中的 $.param() 函数:
function param(object) {
var encodedString = ‘‘;
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
if (encodedString.length > 0) {
encodedString += ‘&‘;
}
encodedString += encodeURI(prop + ‘=‘ + object[prop]);
}
}
return encodedString;
}
发送/接收 JSON
RESTful接口大行其道的今天,JSON数据的发送/接收用的越来越多,这里我们假设需要向服务器发送待更新的数据,在服务器处理完更新之后,返回更新之后的整体数据。这里最适合的HTTP方法是PUT:
jQuery
$.ajax(‘myservice/user/1234‘, {
method: ‘PUT‘,
contentType: ‘application/json‘,
processData: false,
data: JSON.stringify({
name: ‘John Smith‘,
age: 34
})
})
.then(
function success(userInfo) {
// userInfo will be a JavaScript object containing properties such as
// name, age, address, etc
}
);
使用jQuery通过回调的方式提交Ajax请求有时候会把逻辑打散,事实上在不使用defer模式的情况下进行多重ajax请求是非常让人头晕的 事情。jQuery默认的content-type是 application/x-www-form-urlencoded,如果需要使用其他的类型需要手工指定。
译者注:作者这里对 $.ajax() 函数有误解,在 $.ajax() 函数中,data 的内容不需要手工通过 JSON.stringify() 进行序列化,而是可以通过指定 dataType=json 来实现对传入数据的序列化。
Web API
var xhr = new XMLHttpRequest();
xhr.open(‘PUT‘, ‘myservice/user/1234‘);
xhr.setRequestHeader(‘Content-Type‘, ‘application/json‘);
xhr.onload = function() {
if (xhr.status === 200) {
var userInfo = JSON.parse(xhr.responseText);
}
};
xhr.send(JSON.stringify({
name: ‘John Smith‘,
age: 34
}));
由于对JSON的支持问题,上面这段代码只有在IE 8及以上的浏览器中才能正常工作,如果要兼容比较旧的版本,只需要在页面加载json.js就好。
上传文件
在IE 9以及更早的浏览器中,在页面里上传文件的唯一方法是提交一个带有 <input type="file">
标签的表单,在这里没有Web API可以使用。所以我们只能在现代浏览器中讨论上传文件,这里会用到File API。
有两种方法可以上传文件,第一种是使用 <form enctype="multipart/form-data">
将文件作为HTTP请求的一部分上传。第二种是将文件作为请求的整体上传到服务器,两种方法都需要使用 File 或 Blob 对象。
假设上传文件的HTML标签是:
<input type="file" id="test-input">
jQuery
首先可以通过 multipart/form-data 的方式上传文件:
var file = $(‘#test-input‘)[0].files[0],
formData = new FormData();
formData.append(‘file‘, file);
$.ajax(‘myserver/uploads‘, {
method: ‘POST‘,
contentType: false,
processData: false,
data: formData
});
第二种方式:
var file = $(‘#test-input‘)[0].files[0];
$.ajax(‘myserver/uploads‘, {
method: ‘POST‘,
contentType: file.type,
processData: false,
data: file
});
两种方式中,都使用了 processData: false 来防止jQuery对文件内容进行编码。
XMLHttpRequest
第一种方式,multipart/form-data:
var formData = new FormData(),
file = document.getElementById(‘test-input‘).files[0],
xhr = new XMLHttpRequest();
formData.append(‘file‘, file);
xhr.open(‘POST‘, ‘myserver/uploads‘);
xhr.send(formData);
第二种方式:
var file = document.getElementById(‘test-input‘).files[0],
xhr = new XMLHttpRequest();
xhr.open(‘POST‘, ‘myserver/uploads‘);
xhr.setRequestHeader(‘Content-Type‘, file.type);
xhr.send(file);
跨域请求
译者注:jQuery及原生API的Ajax的跨域请求展开是一个很长的话题,原作者在这里介绍的并不详细,因此只是对原文稍加翻译,译者或许会在后面补一篇专门针对跨域Ajax的介绍。
跨域请求,特别是IE 8及更早浏览器中的跨域请求这个专题展开讨论会很复杂,我们这里并不关心所有的细节,只讨论最基本的同源策略和跨域请求。Mozilla 开发者网络里可以找到关于跨域请求的详细说明。
在现代浏览器中,跨域请求和非跨域请求在代码实现层面没有太大区别,只需要在服务器上做好相应设置,浏览器会完成剩下的判断和处理工作。跨域中还有 一个需要注意的问题是,cookie 在默认情况不会被跨域请求携带,必须在 XMLHttpRequest 中指定 withCredentials 标记才会提交跨域cookie。
jQuery
$.ajax(‘http://someotherdomain.com‘, {
method: ‘POST‘,
contentType: ‘text/plain‘,
data: ‘sometext‘,
beforeSend: function(xmlHttpRequest) {
xmlHttpRequest.withCredentials = true;
}
});
XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open(‘POST‘, ‘http://someotherdomain.com‘);
xhr.withCredentials = true;
xhr.setRequestHeader(‘Content-Type‘, ‘text/plain‘);
xhr.send(‘sometext‘);
jQuery实际上变成了一个头痛处理当我们需要发送一个跨域ajax请求在IE8或IE9。如果你使用jQuery为此,你真的想把一个方形的圆孔。
要理解为什么jQuery是一个贫穷的适合跨源请求在IE9和IE8,重要的是要了解几个低级点:
跨 源在IE8,IE9只能发送ajax请求使用IE-proprietary XDomainRequest运输。我将保存咆哮等为什么这是一个巨大的错误IE开发团队的另一个博客。无论如何,XDomainRequest XMLHttpReqest的精简版本,它必须使用时跨源在IE8,IE9的ajax请求。阅读更多关于(重大)限制在这个交通,Eric法律的MSDN 文章阅读。
jQuery的ajax方法(和所有相关的别名)只是包装XMLHttpRequest。它有一个硬依赖XMLHttpRequest。
所 以,你需要使用XDomainRequest IE8/9发送跨源的请求,但是jQuery。ajax是硬编码使用XMLHttpRequest。这是一个问题,并解决它在jQuery的背景下是不会 愉快的。事实上,它是不愉快的,没有一个头脑清醒的人会这样做。幸运的是,对于那些决意要为这种类型的调用使用jQuery,有一些jQuery插件,将 “修复”。从本质上讲,jQuery插件必须覆盖的ajax请求发送/通过美元的处理逻辑。ajaxTransport方法。
但是,发送ajax请求在IE8/9没有jQuery非常简单。事实上,即使你是一个铁杆jQuery的球迷,你应该这样做:
// For cross-origin requests, some simple logic
// to determine if XDomainReqeust is needed.
if (new XMLHttpRequest().withCredentials === undefined) {
var xdr = new XDomainRequest();
xdr.open(‘POST‘, ‘http://someotherdomain.com‘);
xdr.send(‘sometext‘);
}
注意,不能设置任何使用XDomainRequest时请求头。如果你能避免跨源IE8/9 ajax请求,你应该。但是如果你必须熟悉其局限性。
JSONP
这里介绍JSONP并不是推荐你使用它,他会导致一些潜在的安全问题,在现代浏览器中如果可能,还是应该直接使用跨域Ajax。
对于初学JSONP的同学,名字可能会造成一些困扰,事实上这里并没有JSON数据在网络上传输。一个很常见的误解是服务器一定返回包含JSON数据的HTTP包,然后客户端通过JSONP回调来使用。事实上服务器返回的是一段包含了数据结构的脚本代码,而非JSON。
JSONP的实现方式并不优雅,它建立在 <script> 标签不需要遵守同源策略的基础上,同时需要在浏览器端和服务器端都针对JSONP调用书写特殊的代码,服务器通过解析 <script> 的请求地址,生成包含特定数据的文本并返回给客户端。
jQuery
$.ajax(‘http://jsonp-aware-endpoint.com/user‘, {
jsonp: ‘callback‘,
dataType: ‘jsonp‘,
data: {
id: 123
}
}).then(function(response) {
// handle requested data from server
});
我们在讨论一种并不优雅的实现方法,jQuery 中把他包装成和惯常ajax请求类似的格式。事实上下面这段代码更加接接近JSONP的本质:
Without jQuery
window.myJsonpCallback = function(data) {
// handle requested data from server
};
var scriptEl = document.createElement(‘script‘);
scriptEl.setAttribute(‘src‘,
‘http://jsonp-aware-endpoint.com/user?callback=myJsonpCallback&id=123‘);
document.body.appendChild(scriptEl);
可以替代jQuery的库
上面已经阐述过了,在没有辅助库的帮助下,也可以通过原生API完成Ajax的大部分任务,如果为了简化开发流程同时兼顾浏览器兼容性,有几个库可以考虑:
fetch: 这是一个polyfill(抱歉这个没有很好地翻译,可以看这里),能够帮助我们处理浏览器兼容性,在旧的浏览器中提供较好的ajax支持。
xdomain: 跨浏览器实现的跨域请求库,通过 Web Messaging API 来实现。
Lightweight-JSONP: 轻量级JSONP库,提供了对JSONP的支持。
译者注:翻译这篇的时候,有些意见同原作者不完全相同。如果项目大量依赖ajax请求(尤其是跨域请求),那么引入jQuery还是值得的,如果只是选择jQuery的ajax模块,付出的代价还是值得的。
抛弃jQuery:事件
在 这第五期“你不需要jQuery(了)”,我将谈论没有jQuery在浏览器中处理事件。像往常一样,每个部分将介绍jQuery方法,其次是使用本机 Web API,而不是一个解决方案。在事件阅读这篇文章之后,你应该有足够的信心来处理事件不使用jQuery在您自己的项目。
正 如我之前提到的(多次),这篇文章不是关于jQuery恶语相向。jQuery是,毫无疑问,世界上无处不在的web开发。在web开发的早 期,jQuery是需要消除的重要实现差异和错误中发现各种浏览器在处理DOM和web API作为一个整体。同时,Web API相当原始,在某些方面,和jQuery使发展更加直观。
浏览器和 Web API,在过去几年中已经走过了漫长的道路。你可以做很多没有jQuery,我大多避免jQuery过去几年在我的新项目。jQuery方法可能需要减少 击键,或在某些情况下看起来更加优雅。不错,但是,这个博客的意义并不是帮助你减少击键的数量,或写代码更美丽。
我 之前写的几篇涉及选择元素,DOM操作和ajax请求。在这些情况下,Web API相当优雅,是通过使用jQuery。处理事件,然而,当Web API是公认缺乏优雅和方便部门,在某些情况下甚至在现代浏览器。没关系,因为这个博客是为了帮助你理解Web API,这样您就可以避免盲目地根据jQuery开发Web应用程序。当你依赖库开发一个大型应用程序的一部分,你最好理解低级API是如何工作的。否则 你会发现故障排除意外的行为,将不可避免地不时弹出非常令人沮丧。请记住,jQuery也有自己的缺陷。
这 篇文章在事件将主要关注在现代浏览器事件处理。我们将现代浏览器定义为任何更新的(并包括)Internet Explorer 9。这是一个普遍接受的定义,根据我的经验。说,这篇文章在事件还将说明jQuery是Internet Explorer 8普遍支持的时候,尤其重要,你可能要考虑如何把事件库,甚至jQuery,帮助如果你在不寻常的和不幸的位置需要支持Internet Explorer 8以上的新web应用程序。
记住,例子,下面描述,和比较不代表详尽的参考资料。这都是些事来帮助你了解Web API提供了。jQuery和Web API允许您执行更复杂的操作的演示。我只是为你提供所需的基础知识/构件构建更复杂的东西。
发送本机(DOM)事件
发送自定义事件
监听事件
移除事件处理程序
修改事件
事件的代表团
键盘事件
鼠标事件
浏览器加载事件
古老的浏览器支持
图书馆要考虑
在本系列的下一个
发送本机(DOM)事件
我们将开始简单的DOM事件,也就是说,事件被定义为W3C文档对象模型的一部分(World Wide Web Consortium)规范。
假设我们有一个锚标记,我们想使用JavaScript编程点击它。
jQuery
$(anchorElement).click();
这很容易!如果我们真的想做这个jQuery方法(无论什么原因),我们需要包装元素,所以我们可以访问jQuery的API,当然可以。
Web API
anchorElement.click();
上面的代码将在任何浏览器工作目前(甚至IE6)。jQuery当然不会帮助我们。代码遵循相同的直观语法如果我们想引发一些其他的DOM事件,如注意力和模糊,或提交<form>
。
发送自定义事件
我们有一个活动,“my-custom-event”,我们需要触发。这个事件必须泡沫在默认情况下,必须由一个可删除的处理程序。
jQuery
$(‘some-element‘).trigger(‘my-custom-event‘);
上面的代码将会触发自定义事件从someElement元素。默认事件将泡沫DOM。jQuery实际上走DOM事件本身,引发任何jQuery-specific事件处理程序和/或功能的每个元素对应于事件名称(如点击“点击”事件())。
Web API
var event = document.createEvent(‘Event‘); event.initEvent(‘my-custom-event‘, true, true); //can bubble, and is cancellable someElement.dispatchEvent(event);
上面的代码将会在所有的浏览器中起作用。但是,createEvent方法文档,在大多数情况下,被弃用。所有的浏览器,不过据我所知,继续支持它。
更“现代”的方法涉及使用CustomEvent构造函数:
var event = new CustomEvent(‘my-custom-event‘, {bubbles: true, cancelable: true}); someElement.dispatchEvent(event);
不幸的是,CustomEvent构造函数不支持任何版本的Internet Explorer。这将工作在所有其他现代浏览器。如果您需要支持Internet Explorer(和我们都有),你需要回到最初的例子在这一节中。在大多数情况下,最好继续使用createEvent如果支持的浏览器。如果更新的浏 览器开始把createEvent从实施Web API,您应该能够很容易地发现这个,转而使用CustomEvent。
监听事件
语法需要消耗一个事件,为现代浏览器DOM或定制,非常类似jQuery和本地方法。对于这个示例,我们将设立一些代码,通知我们当我们感兴趣一个元素被点击(以编程方式或通过用户交互):
jQuery
$(someElement).on(‘click‘, function() { // TODO event handler logic });
你也可以利用点击的方法,你可以注册一个事件处理程序,如果第一个参数是一个处理函数:
$(someElement).click(function() { // TODO event handler logic });
Web API
如前所述,注册一个事件处理程序的语法使用本机浏览器API在现代浏览器(包括IE9)相当类似于jQuery:
someElement.addEventListener(‘click‘, function() { // TODO event handler logic });
注意所有元素(在大多数情况下)继承节点接口,它本身从EventTarget接口继承。第一个版本的ie浏览器,包括支持addEventListener方法EventTarget接口IE9。
移除事件处理程序
你 是否使用jQuery或香草JavaScript,您必须记录你的原始事件处理函数是为了自己。,jQuery提供了一种便利方法消除“所有”某一特定类 型的事件处理程序从一个特定的元素,重要的是要明白,这只会删除所有事件处理程序附加到该元素通过jQuery(忽略任何可能是连接直接通过 addEventListener)。这是由于这样的事实:Web API并不提供任何方式获得注册事件处理程序的列表,也提供了一种盲目删除所有事件处理程序附加到一个元素。
所以,比方说我们之前附加以下单击事件处理程序的元素:
var myEventHandler = function(event) { // handles the event... }
…我们现在想要删除:
jQuery
$(‘some-element‘).off(‘click‘, myEventHandler);
Web API
someElement.removeEventListener(‘click‘, myEventHandler);
修改事件
修改事件的能力通常是指增加或导致事件的事件处理程序才能进入另一个事件处理程序。
防止事件冒泡进一步DOM
在这里,我们希望确保事件,被我们的事件处理程序,不会达到任何附加事件处理程序放置在祖先元素。这将阻止事件冒泡DOM。
jQuery
$(someEl).on(‘some-event‘, function(event) { event.stopPropagation(); });
Web API
someEl.addEventListener(‘some-event‘, function(event) { event.stopPropagation(); });
jQuery和现代Web API之间的语法这是几乎相同的。
防止事件触及任何额外的处理程序附加到当前元素
不仅我们要阻止这一事件触及任何处理程序绑定到元素的祖先,但我们也希望确保没有其他事件处理程序绑定到该元素也打。所以,我们要防止事件进一步的事件处理程序。
jQuery
$(someEl).on(‘some-event‘, function(event) { event.stopImmediatePropagation(); });
Web API
someEl.addEventListener(‘some-event‘, function(event) { event.stopImmediatePropagation(); });
再次,jQuery和现代Web API之间的语法这是出奇的相似。
防止事件触发浏览器定义的一个操作
假设我们有以下元素:
<a href="http://fineuploader.com">Go to Fine Uploader</a>
…我们要防止点击这个锚打开相关页面。这将包括添加锚元素的单击处理程序和指示浏览器不执行其原生/默认动作。
jQuery
$(someAnchor).on(‘click‘, function(event) { event.preventDefault(); });
Web API
someAnchor.addEventListener(‘click‘, function(event) { event.preventDefault(); });
你在这里看到一个模式?处理事件所需的语法开始显得jQuery和Web API之间大致相同。
事件的代表团
假设您有一个列表充满敏感鼠标点击列表项。你可以把每个列表项的单击处理程序。然而,这可能是低效和减缓你的页面与大量的列表项。假设项目动态添加到该列表。现在将一个新的事件处理程序附加到每个新项目列表,添加,变得不那么有吸引力。
这里的解决方案是事件的代表团。也就是说,把一个单击处理程序列表。点击任何一个列表项时,将泡沫其母,容器元素列表。在这一点上,你的一个事件处理程序会打,你可以很容易地确定,通过检查事件对象,列表项点击并做出适当的反应。
的标记列表看起来像这样:
<ul id="my-list"> <li>foo <button>x</button></li> <li>bar <button>x</button></li> <li>abc <button>x</button></li> <li>123 <button>x</button></li> </ul>
如果用户点击“x”按钮,列表项应该从该列表中移除。
jQuery
jQuery将我们的处理程序元素的上下文,最初收到点击(<按钮>)。同样,只有<按钮>元素在这个列表将被检查。
$(‘#my-list‘).on(‘click‘, ‘BUTTON‘, function() { $(this).parent().remove(); });
Web API
我们必须写几行代码没有jQuery,但这仍然不是火箭科学。Web API总是设置事件处理程序的上下文元素接收点击事件,本例中是容器元素列表。相反,我们需要知道哪个元素最初是单击,可以在目标提供的事件对象的属性。 我们还必须确保我们只按照<按钮>点击。
document.getElementById(‘my-list‘).addEventListener(‘click‘, function(event) { var clickedEl = event.target; if(clickedEl.tagName === ‘BUTTON‘) { var listItem = clickedEl.parentNode; listItem.parentNode.removeChild(listItem); } });
记住,这不是优雅的代码,它是关于探索和理解Web API。毕竟,jQuery只是一个Web API包装器。
键盘事件
在这一节中,我将展示如何处理各种键盘事件。我还将展示如何识别哪些特定的关键用户按下。
首先,我们应该花一些时间来确保我们理解的区别一般键盘事件的三种不同类型:
keydown:键已经按但尚未公布。没有默认的动作已经完成。
键盘按键:键已经按字符注册。这个事件会不断火如果关键是下来。
keyup:按键被释放。
假设我们正在构建一个web应用程序,想要一个交互式教程,我们构建通过一个直观的键盘快捷键。在应用程序的任何地方,我们的用户应该能够打开我们的帮助小部件通过Ctrl-H组合键。
jQuery
$(document).keydown(function(event) { if (event.ctrlKey && event.which === 72) { // open help widget } });
Web API
document.addEventListener(‘keydown‘, function(event) { if (event.ctrlKey && event.which === 72) { // open help widget } });
没有明显上涨,在这种情况下,jQuery。即使语法几乎相同的Web API和jQuery。
注册为其他键盘事件遵循类似的模式:
jQuery
$(someElement).keypress(function(event) { // ... }); $(someElement).keyup(function(event) { // ... });
Web API
someElement.addEventListener(‘keypress‘, function(event) { // ... }); someElement.addEventListener(‘keyup‘, function(event) { // ... });
关于键盘事件属性的更多信息和浏览器之间的兼容性的性质这一事件,看看KeyboardEvent文档在Mozilla开发人员网络。
鼠标事件
有很多Web API提供的鼠标事件,如“mousedown”、“mouseenter”和“鼠标悬停”(等等)。它不是特别有趣的展示如何注册和处理这些事件在jQuery和Web API。就像键盘事件,两者之间的语法几乎是相同的。
相反,我要专注于一个特殊的事件,是jQuery API的一部分。当然,这里的目标是创建您自己的代码通过使用普通的ole Web API sans jQuery。我也将向您展示如何做到这一点。
jQuery的鼠标事件
jQuery提供了一种方法来通知你当鼠标指针悬停在一个特定的元素,然后再当鼠标指针离开这个元素。例如:
$(‘some-element‘).hover( function hoverIn() { // mouse is hovering over this element }, function hoverOut() { // mouse was hovering over this element, but no longer is } );
我们可以做同样的事情与Web API很容易:
someEl.addEventListener(‘mouseover‘, function() { // mouse is hovering over this element }); someEl.addEventListener(‘mouseout‘, function() { // mouse was hovering over this element, but no longer is });
如前所述,鼠标事件处理程序是相当简单的Web API。在鼠标事件的更多信息,看一下MouseEvent接口文档在Mozilla开发人员网络。
浏览器加载事件
在谈到“负载”事件在浏览器中,我们可能会试图回答下列问题中的任何一个:
当所有元素在页面完全加载并呈现应用风格吗?
这个事件应该被解雇后:
所有标记已经被放置在页面上
所有已加载样式表
所有图片加载
iframes都满载
jQuery
$(window).load(function() { // page is fully rendered })
Web API
window.addEventListener(‘load‘, function() { // page is fully rendered });
当所有静态标记被放置在页面吗?
这个事件应该火毕竟标记已被放置在页面上。
jQuery
$(document).ready(function() { // markup is on the page });
注意,您还可以在jQuery实现同样的事情:
$(function() { // markup is on the page });
Web API
document.addEventListener(‘DOMContentLoaded‘, function() { // markup is on the page });
请注意,您可能会想要确保你的脚本,寄存器DOMContentLoaded事件处理程序内放置任何样式表之前<链接>标签,因为加载这些stylsheets将阻止任何脚本执行,延长DOMContentLoaded事件直到定义的样式表内加载。
当有一个特定的元素在页面上满载吗?
除了窗口中,负载的事件相关联的元素数量,如< img >、<链接>和<脚本>。最常见的使用这个事件的窗外是确定当加载一个特定的形象。
jQuery
$(‘img‘).load(function() { // image has successfully loaded });
你也可以确定图像未能加载:
$(‘img‘).error(function() { // image has failed to load });
Web API
img.addEventListener(‘load‘, function() { // image has successfully loaded });
如果图像就无法加载吗?
img.addEventListener(‘error‘, function() { // image has failed to load });
正如我们所看到的很多次,jQuery和Web API之间的语法在现代浏览器是惊人地相似。
Ancient Browser支架
这就是我谈论时,jQuery确实为web应用程序所需的库。回来的时候共同支持IE8或更老。当时,Web / DOM API是有点混乱在某些情况下。在处理事件时尤其如此。下面,我将简要讨论一些可以处理事件的方式在老式浏览器中使用香草JavaScript。我要限制 这种IE7和IE8。IE6是死在这一点上。虽然很多,如果不是全部,这些例子也应该适用于IE6。
监听事件
someElement.attachEvent(‘onclick‘, function() { // TODO event handler logic });
你会注意到这两个截然不同的差异和现代浏览器的方法。
我们必须依靠attachEvent代替addEventListener。
事件名称必须包括“on”的前缀。
也 许你想知道你可以很容易地和以编程方式使用正确的事件处理方法,基于浏览器的功能。对于我们大多数人来说发展中专门为现代浏览器应用程序,这不是一个问 题。但是,如果由于某种原因,你必须目标古老的浏览器,IE8等(或以上),您可以使用下面的代码来注册一个事件在任何浏览器:
function registerHandler(target, type, callback) { var listenerMethod = target.addEventListener || target.attachEvent, eventName = target.addEventListener ? type : ‘on‘ + type; listenerMethod(eventName, callback); } // example use registerHandler(someElement, ‘click‘, function() { // TODO event handler logic });
一件事:如果你想要删除一个事件处理程序在IE8,老,您必须使用detachEvent代替removeEventListener。因此,跨浏览器方法删除一个事件侦听器可能看起来像这样:
function unregisterHandler(target, type, callback) { var removeMethod = target.removeEventListener || target.detachEvent, eventName = target.removeEventListener ? type : ‘on‘ + type; removeMethod(eventName, callback); } // example use unregisterHandler(someElement, ‘click‘, someEventHandlerFunction);
表单字段更改事件
旧版本IE有一些严重的更改事件的不足。这里有两个大的,想到:
更改事件没有泡沫。
复选框和单选按钮可能不会引发更改事件。
重要的是要知道第二问题上面也可再生的使用jQuery IE7/8时在相当长的一段时间。据我所知,当前版本的jQuery(~ 1.10 +)做妥善解决这一问题。
# 1来解决问题,你必须改变处理程序直接附加到任何您想要监控的表单字段。事件的代表团是不可能的。问题# 2,你最好的选择可能是无线电/复选框字段附加了一个单击处理程序而不是依靠更改事件。
事件对象
首先,在IE8和老的事件对象不是直接传递给注册事件处理程序(请记住,您必须使用attachEvent)。相反,事件设置为一个属性窗口。所以,如果你正在编写事件处理程序可以执行在现代或古代浏览器,你需要这样的覆盖两种情况下:
function myEventHandler(event) { var actualEvent = event || window.event; // handle actualEvent }
此外,一些事件对象实例的属性在老式浏览器略有不同。例如,尽管在现代浏览器的目标可以通过检查的目标属性事件实例,IE8及以上为该元素包含一个不同的属性,srcElement命名。所以,现在你的跨浏览器的事件处理程序如下:
function myEventHandler(event) { var actualEvent = event || window.event, actualTarget = actualEvent.target || actualEvent.srcElement // handle actualEvent & actualTarget }
控制你的事件而言,stopPropagation方法不可用在IE8,年长的一个事件对象实例。如果你想阻止事件冒泡,你必须设置cancelBubble属性事件。一个跨浏览器的解决方案看起来像这样:
function myEventHandler(event) { var actualEvent = event || window.event; if (actualEvent.stopPropgation) { actualEvent.stopPropagation(); } else { actualEvent.cancelBubble = true; } }
E8,老也没有stopImmediatePropagation方法。没有太多工作要做。我不认为缺乏这种方法作为一大损失,像使用 stopImmediatePropagation看起来像我的代码味道因为这个调用的行为是完全依赖于多个事件处理程序附加到元素的顺序。
浏览器加载事件
在加载事件窗口似乎相当古老的浏览器功能,DOMContentLoaded事件内直到版本才支持Internet Explorer 9。对于旧版本,解决方案是有点组装机。如果DOMContentLoaded真的对你很重要,你必须支持IE8,老,考虑在小 contentloaded拉。js脚本。
Mozilla开发人员所需的网络提供了一个很好的解释逻辑模拟DOMContentLoaded古代事件浏览器内的行为:
Internet Explorer 8支持readystatechange事件,这可以用来检测时,DOM是准备好了。在早期版本的Internet Explorer,这种状态可以被反复试图执行document.documentElement.doScroll(“左”);,这段代码会抛出一个错 误,直到DOM已经准备好了。
当然,还有其他的陷阱在事件处理的背景下在处理古老的浏览器。我决定上面概述的一些较常见。