上周五,大约 300 名开发者前往英国牛津参加 jQuery UK 大会,并学习他们最喜欢的 JavaScript 库的所有最新动态。想象一下,当我走上舞台告诉他们,如今 jQuery 用来做很多事情其实并不需要它的时候,他们有多么惊讶。如果你想了解更多关于演讲本身的信息,有一个 详细的报告、幻灯片和音频录制 可供参考。
我想表达的是,像 jQuery 这样的库首先是为了给开发者提供一个公平的竞争环境。我们不应该需要了解每个浏览器的怪癖,而使用库可以让我们专注于手头任务,而不是它在 10 年前的浏览器中如何失效。
jQuery 革命性的网页设计新思路基于两大要点:通过 CSS 选择器而不是笨拙的 DOM 方法访问文档,以及 JavaScript 命令的链式调用。然后,jQuery 继续简化事件处理和 Ajax 交互,并实现了 缓动方程 以实现流畅而美丽的动画。
然而,这种简洁性是有代价的:开发者似乎忘记了一些非常简单的技巧,这些技巧允许你编写非常简洁易懂的 JavaScript,而无需依赖 jQuery。其中,最强大的技巧包括事件委托和为父元素分配类,并将主要工作留给 CSS。
事件委托
事件委托 意味着,与其为元素中的每个子元素应用事件处理程序,不如为父元素分配一个处理程序,并让浏览器为你完成剩下的工作。事件在文档的 DOM 中冒泡,并在你想要获取的元素及其每个父元素上发生。这样,你只需与事件的目标进行比较,即可获取你想要访问的元素。假设你的文档中有一个待办事项列表。你需要的全部 HTML 代码是
- Go round Mum's
- Get Liz back
- Sort life out!
为了向这些列表项添加事件处理程序,jQuery 初学者可能会尝试执行 $('#todo li').click(function(ev){...});
或——更糟糕的是——向每个列表项添加一个类,然后访问这些类。如果你使用事件委托,那么在 JavaScript 中你只需要
document.querySelector('#todo').addEventListener( 'click',
function( ev ) {
var t = ev.target;
if ( t.tagName === 'LI' ) {
alert( t + t.innerHTML );
ev.preventDefault();
}
}, false);
较新的浏览器具有 querySelector
和 querySelectorAll
方法(此处查看支持情况),它们允许你通过 CSS 选择器访问 DOM 元素——这是我们从 jQuery 中学到的东西。我们在这里使用它来访问待办事项列表。然后,我们为列表应用 click
的事件监听器。
我们使用 ev.target
读取出哪个元素被点击,并将其 tagName
与 LI
进行比较(此属性始终为大写)。这意味着当用户例如点击列表本身时,我们永远不会执行其余代码。我们调用 preventDefault()
以告诉浏览器不要执行任何操作——我们现在接管了。
事件委托的好处在于,你现在可以添加新项目,而无需重新分配处理程序。由于主要点击处理程序位于列表上,因此新项目会自动添加到功能中。请在 此 fiddle 或下面的嵌入中尝试一下
将样式和 DOM 遍历留给 CSS
jQuery 的另一个主要用例是同时访问大量元素,并通过使用 jQuery 的 css()
方法操作其 styles
集合来更改其样式。这看似很方便,但也令人烦恼,因为你将样式信息放在 JavaScript 中。如果以后需要重新品牌化怎么办?人们在哪里找到要更改的颜色?为有问题的元素添加一个类并将其余工作留给 CSS 要简单得多。如果你仔细想想,很多时候我们在 jQuery 和样式文档中重复相同的 CSS 选择器。这似乎是多余的。
过去,添加和删除类有点像噩梦。执行此操作的方法是使用 DOM 元素的 className
属性,该属性包含一个字符串。然后,由你决定在该字符串中查找某个类名,并通过添加到或对字符串使用 replace()
来删除和添加类。同样,浏览器从 jQuery 中学习,现在拥有一个 classList 对象(此处查看支持情况),它允许轻松操作应用于元素的 CSS 类。你可以使用 add()
、remove()
、toggle()
和 contains()
。
这使得为大量元素设置样式并将其单列出来以进行不同的样式设置变得非常容易。例如,假设我们有一个内容区域,并且希望一次显示一个。我们可能会想遍历这些元素并进行大量比较,但我们真正需要的只是分配类并将其余工作留给 CSS。假设我们的内容是一个指向文章的导航。这在所有浏览器中都能正常工作
Profit plans
Step 1: Collect Underpants
Make sure Tweek doesn't expect anything, then steal underwear
and bring it to the mine.
Step 2: ???
WIP
Step 3: Profit
Yes, profit will come. Let's sing the underpants gnome song.
现在,为了隐藏所有文章,我们只需为文档的 body 分配一个“js”类,并将内容部分中的第一个链接和第一篇文章存储在变量中。我们为每个元素分配一个名为“current”的类。
/* grab all the elements we need */
var nav = document.querySelector( '#nav' ),
content = document.querySelector( '#content' ),
/* grab the first article and the first link */
article = document.querySelector( '#content article' ),
link = document.querySelector( '#nav a' );
/* hide everything by applying a class called 'js' to the body */
document.body.classList.add( 'js' );
/* show the current article and link */
article.classList.add( 'current' );
link.classList.add( 'current' );
结合简单的 CSS,这会将它们全部隐藏在屏幕外
/* change content to be a content panel */
.js #content {
position: relative;
overflow: hidden;
min-height: 300px;
}
/* push all the articles up */
.js #content article {
position: absolute;
top: -700px;
left: 250px;
}
/* hide 'back to top' links */
.js article footer {
position: absolute;
left: -20000px;
}
在这种情况下,我们向上移动文章。我们还隐藏了“返回顶部”链接,因为当我们隐藏和显示文章时,它们是多余的。要显示和隐藏文章,我们只需为要显示的元素分配一个名为“current”的类,该类会覆盖原始样式。在这种情况下,我们将文章向下移动。
/* keep the current article visible */
.js #content article.current {
top: 0;
}
为了实现这一点,我们只需要对导航进行简单的事件委托即可
/* event delegation for the navigation */
nav.addEventListener( 'click', function( ev ) {
var t = ev.target;
if ( t.tagName === 'A' ) {
/* remove old styles */
link.classList.remove( 'current' );
article.classList.remove( 'current' );
/* get the new active link and article */
link = t;
article = document.querySelector( link.getAttribute( 'href' ) );
/* show them by assigning the current class */
link.classList.add( 'current' );
article.classList.add( 'current' );
}
}, false);
这里的简洁性在于,链接已经指向具有这些 ID 的元素。因此,我们只需要读取被点击的链接的 href
属性即可。
请在 此 fiddle 或下面的嵌入中查看最终结果。
将视觉效果保留在 CSS 中
结合 CSS 过渡 或动画(此处查看支持情况),可以通过非常简单的方式使其更加流畅
.js #content article {
position: absolute;
top: -300px;
left: 250px;
-moz-transition: 1s;
-webkit-transition: 1s;
-ms-transition: 1s;
-o-transition: 1s;
transition: 1s;
}
现在,过渡只需一秒钟即可从没有“current”类的状态平滑地过渡到具有该类的状态。在我们的案例中,向下移动文章。你可以通过编辑 CSS 添加更多属性——无需更多 JavaScript。请在 此 fiddle 或下面的嵌入中查看结果
由于我们还在链接上切换 current 类,因此我们可以做更多的事情。通过使用 CSS 生成的内容和 :after
选择器(此处查看支持情况),可以轻松添加诸如“您当前所在位置”之类的视觉效果。这样,你就可以添加视觉上的增强功能,而无需在 JavaScript 中生成 HTML 或使用图像。
.js #nav a:hover:after, .js #nav a:focus:after, .js #nav a.current:after {
content: '➭';
position: absolute;
right: 5px;
}
请在 此 fiddle 或下面的嵌入中查看最终结果
此技术的好处在于,我们将所有外观和感觉都保留在 CSS 中,并使其更易于维护。通过使用 CSS 过渡和动画,你还可以利用硬件加速。
请试一试,好吗?
所有这些内容都适用于我们如今使用的浏览器,并且可以使用 polyfill 使其在旧浏览器中也能工作。但是,并非所有内容都需要应用于旧浏览器。作为 Web 开发人员,我们应该着眼未来,而不是迎合过时的技术。如果我上面展示的内容在 IE6 中回退到服务器端解决方案或页面重新加载,没有人会知道的。让我们构建自动扶梯解决方案——当技术有效时,它会很流畅,但在技术无效时,它仍然可以作为楼梯使用。
翻译
关于 Chris Heilmann
HTML5 和开放 Web 的布道者。让我们来修复它!
30 条评论