7.1.10 Element 实用工具*
YUI Element 实用工具是 HTMLElement 的包装器。比较值得注意的地方在于,它允许延迟分配事件监听程序。它的实现方法是,监听何时把 HTMLElement 添加到 DOM ,只有在这个时候才尝试将事件附加到这些对象上。当 HTMLElement 变成可用时,就会触发 contentReady 事件。最终,它简化了属性的设置和获取。
要想使用 Element 实用工具,需要像下面这样实例化 Element 对象:
var el = new YAHOO.util.Element("foo");
在使用上面示例中实例化的对象时,可以把一个事件处理程序添加到 el 元素,即使 ID 为 foo 的 HTMLElement 尚未添加到 DOM 中。在下面的示例中,当 ID 为 foo 的元素可用时就会触发一个警告框。然后,它将一个 mouseover 事件处理程序附加到该元素,该处理程序负责把元素的文本颜色修改为红色。触发所有这些行为的就是一个两秒的定时器,它向 DOM 中插入一个 ID 为 foo 的 div 元素。
<html>
<head><title>YUI Element Utility</title></head>
<body>
<script src="yahoo-dom-event.js"></script>
<script src="element-min.js"></script>
<script>
var YE = YAHOO.util.Event,
YEL = YAHOO.util.Element,
YX = YAHOO.example;
YX.el = new YEL("foo");
YX.el.on("contentReady", function(){
alert("foo is here!");
});
YX.el.on("mouseover", function(){
this.setStyle("color", "red");
});
setTimeout(function(){
var foo = document.createElement("div");
foo.id = "foo";
foo.appendChild(document.createTextNode("foo"));
document.body.appendChild(foo);
}, 2000);
</script>
</body>
</html>
7.1.11 Selector 实用工具
有时候,使用 DOM 方法获取 HTMLElement 可能显得比较笨拙,常常需要几行代码来过滤位于特定节点层次中或者具有特定属性的某个元素。但是,如果熟悉 CSS 选择器语法,就会知道只需要一行简单的代码即可完成该操作。
例如,.foo 可以定位所有具有CSS类名foo的元素,而 #foo .bar 则可以定位具有CSS类名 bar 的所有元素,并且这些元素是 ID 为 foo 的元素的子元素。
根据这个思想,对于下面的 JavaScript 代码块:
<html>
<head><title>YUI Selector Utility</title></head>
<body>
<h1>YUI Selector Utility</h1>
<div id="foo">
<ul><li class="first">This is the first list item</li><li class="second">Here's the second</li><li class="third">And this one's the third</li></ul>
</div>
<script>
var foo = document.getElementById("foo");
var listItems = foo.getElementsByTagName("li");
for(var i=0; listItems[i]; i+=1){
if(listItems[i].className === "second"){
listItems[i].style.color = "red";
}
}
</script>
</body>
</html>
使用 Selector 实用工具就可以方便地将其编写成如下的代码:
<script src="yahoo-dom-event.js"></script>
<script src="selector-min.js"></script>
<script>
var YS = YAHOO.util.Selector,
YX = YAHOO.example;
YX.second = YS.query(".second", null, true);
YX.second.style.color = "red";
</script>
YUI Selector 实用工具采用了 CSS 选择器语法,返回一组匹配的元素。在上面的示例中,传入一个附加的 Boolean 参数来告诉引擎只返回它找到的第一个元素。这样就可以避免在一个已经知道只含有一项的数组中选择第一项,这是一个多余的步骤。第二个参数是可以传入的起始节点 ID 或引用,这就允许通过减少需要检查的节点数量实现更好的性能。可以将第二个参数设置为 null 或 undefined ,这样它就默认为 Selector.document,而 Selector.document 实际上就是指向文档自身的引用,但是不能将其作为参数传入,因为代码首先检查传入的值是否具有有效的标记名称 (document就无效) 。
7.2 操作内容
仅仅从 DOM 中检索元素并没有什么用处,除非对这些元素进行一些处理。对于移动元素、修改元素或者只是添加新的元素这样的操作,也同样可以通过 YUI 的 DOM 集合来完成。
7.2.1 insertBefore 方法
使用 DOM 提供的 insertBefore 方法时,不仅要求给出一个新节点和一个引用节点,而且还需要在相同引用节点的父节点处调用该方法。
var newNode = refNode.parentNode.insertBefore(newNode, refNode);
YUI 的 insertBefore 方法不需要两次涉及引用节点,而且允许两个参数都是 ID 字符串类型或实际的节点引用。如果现有的参数只属于其中一种类型,那么这就会使编码变得简单一些。与原生 DOM 方法类似,YUI 的 insertBefore 方法也返回一个指向新节点的引用。
var newNode = YAHOO.util.Dom.insertBefore(newNode, refNode);
7.2.2 insertAfter 方法
虽然 DOM 提供了 insertBefore 方法,但是没有提供 insertAfter 方法。要在现有的节点之后插入一个新节点,就需要编写额外的代码。
var newNode = refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
如果引用节点有一个 nextSibling (并非总是这种情况),那么这段代码可以工作。因此,YUI 将这种额外的检查和引用全部包装进 insertAfter 方法中。
var newNode = YAHOO.util.Dom.insertAfter(newNode, refNode);
7.2.3 处理类名
在如今的复杂 Web 应用程序中,节点只有一个 CSS 类名的情形已经成为历史。下面是一个简单的示例:
<ul id="nav">
<li class="home selected hover"><a href="/index.html">Home</a></li>
<!-- More list items here --->
</ul>
上面的示例演示了一个元素如何能够拥有多个 CSS 类名,并且每个 CSS 类名都是有效的和必需的。然而, W3C DOM 规范并没有提供用于添加、替换和删除选择的 CSS 类名的原生方法,唯一用于处理 CSS 类名的方式就是通过赋值。
el.className = "home";
我们可以方便地添加另一个 CSS 类名。
但是,无法知道某个 CSS 类名是否已经存在。YUI 的 DOM 集合提供了一组方法用于安全地操作 CSS 类名。而且,这些方法均接受元素、元素 ID 或元素数组作为待处理对象。因此,如果有一整批的元素都需要在其上执行某种 CSS 类名操作,那么可以将整个数组直接作为 el 参数传入。
1.hasClass 方法
检查一个元素是否具有某个特定的 CSS 类名并不像看起来那么简单。元素并非总是只有一个CSS类名:
<li class="home selected hover">
上面的代码使得类似于如下的检查变得不确定:
if(el.className === "selected") { // return false
该元素的CSS类名之一可能就是 selected 。但是,在这里它有多个 CSS 类名,这使得该 if 语句返回 false 。检查 indexOf 也同样可能变得不确定:
if(el.className.indexOf("select") !== -1) {
这不可能成为检查某个 CSS 类名是否存在的可靠方法,因为即使部分匹配,它也会返回 true 。即使这个元素具有 CSS 类名 selected、selection 或 selectable ,检查select 也会返回 true 。这里需要一种算法将 className 字符串划分成独立的 CSS 类名并检查每个 CSS 类名。这就是 hasClass 方法完成的工作。
if(YAHOO.util.Dom.hasClass(el, "select")) {
只有这个元素的 className 特性中出现完整的单词 select 时,这行代码才会返回 true 。
2.addClass 方法
前面曾经提到,可以方便地添加 CSS 类名:
el.className += " hover";
但是麻烦的地方在于,无法知道刚才分配的 CSS 类名是否已经存在,而且 className 特性是字符串,因此可能只是连接该 CSS 类名。因此,上面的代码可能导致如下的结果:
<li class="hover hover">
在试图移除 CSS 类名 hover 时,这可能会导致程序错误,因为这两个 CSS 类名中只有一个会被删除,而该元素还剩下一个不希望具有的CSS类名 hover 。YUI 的 addClass 方法首先进行检查以确保正在添加的 CSS 类名并不存在。如果该 CSS 类名并不存在,该方法就会将其添加进去,并返回 true 。如果该 CSS 类名已经存在,该方法就会返回 false 而不添加任何 CSS 类名。
YAHOO.util.Dom.addClass(el, "hover");
3.removeClass 方法
将一个 CSS 类名从元素中删除可能是处理类名时比较困难的任务之一。这里必须进行实际的字符串处理。不仅要找到表示该CSS类名的这个单词,而且需要重新构造 className 属性的内容以减去要删除的 CSS 类名。一旦完成了这些操作,就需要将这个新的字符串重新分配给 className 属性,有效地使用新字符串覆盖旧字符串。YUI 将整个过程缩减为一个简单的方法 removeClass 。
YAHOO.util.Dom.removeClass(el, "hover");
4.replaceClass 方法
有时候并不需要将 CSS 删除,而是只要将其替换。这正式 YUI 的 replaceClass 方法发挥作用的地方。如果没有找到需要替换的 CSS 类名,该方法就会将新的 CSS 类名添加进去并返回 true 。如果该方法确实找到要替换的 CSS 类名,它就会将其替换并返回 true 。如果某个元素刚好有多个相同的 CSS 类名,那么该方法会将它们全部替换成新的值。
YAHOO.util.Dom.replaceClass(el, "open", "closed");
7.2.4 setStyle 方法
设置 DOM 元素的样式值相对简单。但是,Internet Explorer 6 通过特殊的 filter 规则来管理透明度,而 Firefox、Opera 和 Safari 并不是这样操作。此外,浮动元素的使用也有些麻烦,这是因为 float 属于保留字。因此,Internet Explorer 将其称为 styleFloat ,而其他浏览器则将其称为 cssFloat 。这些特性不仅令人讨厌,而且可能会造成程序错误。必须记住并容忍它们可能是一件痛苦的事情。此外,CSS 规则名称语法要求使用连字符分隔单词,但是 JavaScript 使用驼峰式大小写 (camel case) 的形式。
<style>
#foo {
font-size: 2em;
font-family: arial;
float: right;
}
</style>
<script>
foo.style.fontSize = "2em";
foo.style.fontFamily = "arial";
foo.style.cssFloat = "right";
</script>
YUI 的 DOM 集合方法 getStyle 和 setStyle 将这些问题全部规范化。
<script>
YAHOO.util.Dom.setStyle(foo, "font-size", "2em");
YAHOO.util.Dom.setStyle(foo, "font-family", "arial");
YAHOO.util.Dom.setStyle(foo, "float", "right");
YAHOO.util.Dom.setStyle(foo, "opacity", "0.5");
</script>
注意:
一定要将这段脚本放在 DOM 中 ID 为 foo 的元素之后,最好是位于 结束标记 </body> 之前。否则就无法找到该元素,并且这段代码不会运行。
7.2.5 getStyle 方法
获取一个元素的样式可能比较棘手,这是因为 JavaScript 的 style 对象只保存通过 JavaScript 设置的值。换句话说,通过常规 CSS 代码设置的样式值不会由 style 对象获取。
<script>
el.style.width = "100px";
alert(el.style.width); // returns "100px" because it was just set;
alert(el.style.height); // returns "";
</script>
获取元素的样式值 (不管是否在 style 对象中设置) 的方式是使用 getComputedStyle 方法,但是该方法不适用于 Internet Explorer 。在这种情况下,要使用的方法是 currentStyle ,但是该方法没有考虑 YUI 方法 setStyle 中所涵盖的全部规范化操作。同样,YUI 的 DOM 集合方法 getStyle 为解决这个问题提供了一种跨浏览器解决方案。
<script>
YAHOO.util.Dom.getStyle(foo, "font-size");
</script>
7.2.6 setXY 方法
设置元素的 x 和 y 坐标并非总是简单的事情,但是使用 setXY 方法确实使其变得简单。在大部分情况下,该方法运行良好。但是,如果元素深度嵌套在某个反常的布局内部,那么setXY方法不会输出想要的结果。然而,它确实会尝试第二次运行,并通过第三个 Boolean 参数来允许控制是否再次尝试执行。在大多数情况下,该方法还是相当可靠的。
// set foo's x to 10px and y to 100px and don't retry
YAHOO.util.Dom.setXY(foo, [10, 100], true);
还有单独的 setX 和 setY 方法,它们的语法与 setXY 方法相同,但 x 和 y 值参数是作为数值 (而非数组) 传入的。因此,可以将上面的示例改写为:
YAHOO.util.Dom.setX(foo, 10);
YAHOO.util.Dom.setY(foo, 100);
YUI 3 中新增功能
除了新的名称空间 (现在是 YUI 而不是 YAHOO) 之外,YUI 3 还引入了一种全新的 DOM 遍历和操作方式。它将 DOM 节点看作 YUI 节点对象。节点对象具有方法和属性,可用于更加方便地访问 DOM 并与其交互。一个重大的变化就是 YUI 使用了新的 get 方法,它与 YUI 2.x 版本的 selector 方法类似。下面这个示例使用了 get 方法 (请注意如何在新的脚本依赖项加载器 use 中包装它)。
YUI().use('node', function(Y){
var node1 = Y.get('#main');
var node2 = Y.get(document.body);
});
来源:这个代码示例来自 YUI 3 Node 页面。