JavaScript DOM笔记03

改进的DOM操作

系列文章

平稳退化的要求

平稳退化(graceful degradation)要求正确使用JavaScript脚本,使浏览器能在不支持JavaScript的情况下仍然可以顺利访问网站,虽然某些功能无法使用,但基本操作仍然能顺利完成。

可以使用 window 对象的 .open(url, name, features) 方法来创建一个新的浏览器窗口。该方法的三个参数都是可选的:

open 方法的功能对文档没有任何影响,只与浏览环境(即窗口对象)有关。

以下函数用于打开一个320像素宽、480像素高的新窗口"popup":

function popUp(winurl) {
    window.open(winurl, "popup", "width=320,height=480");
}

由于已经给新窗口命名,因此传入新的URL时,会把新窗口的现有文档替换,而不是去创建一个新的窗口。

可以在链接中使用JavaScript伪协议。javascript: 伪协议可以调用JavaScript函数。

以下通过伪协议控制一个链接:

<a href="javascript: popUp('http://www.w3.org');">Example</a>

不过通过伪协议调用JavaScript代码的做法很不好:旧的浏览器会尝试打开这个链接但是失败,支持但禁用JavaScript功能的浏览器什么也不做。

结点元素的属性也可以通过结点对象的同名属性来访问,例如 someimg.src

因此控制一个链接行为的最好方式是提供真实存在的URL地址,但是通过事件控制函数去拦截它的默认行为:

<a href="http://www.w3.org"
   onclick="popUp(this.href); return false;">Example</a>

渐进增强的要求:用一些额外的信息包裹原始数据。这样创建的网页基本符合平稳退化的原则。

事件可以在JavaScript里处理。元素的事件名将作为对象的一个属性,例如 elem.onclick

可以将一个匿名函数对象赋值给事件处理函数。匿名函数是即用即声明的函数,其模板如下:

element.event = function(params) {
    statements
}

注意匿名函数不能单独存在,必须被调用或赋值给一个函数对象。

这样,通过独立的JavaScript来控制文档行为的方式如下:

var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
    if (links[i].getAttribute("class") == "popup") {
        links[i].onclick = function() {
            popUp(this.href);
            return false;
        }
    }
}

这些语句将在JavaScript被加载时立即执行。因此,将 <script> 标签放在文档底部 </body> 的意义在于:确保所有脚本加载完全,文档DOM模型完整,getElementsByTagName() 等方法能获取完整的结点对象。

必须让JavaScript代码在HTML文档全部加载到浏览器之后马上开始执行。document 对象也是 window 对象的一个属性。当 window 对象触发 onload 事件时,document 对象已经存在。

可以自定义 window.onload 函数,将这些修改文档行为的语句打包进去,最后返回 false ,确保DOM可以正常被修改。

向后兼容

把某个方法放在 if 语句里,根据求值结果来判断方法是不是存在,进而执行相应的语句,这称为对象检测(object detection)。这样很容易把不支持某个特定DOM方法的浏览器检测出来:

if (object.method) {
    action
}

注意方法名后不要加上圆括号,因为检测的是一个方法对象而不是方法调用。

性能考虑

应该尽量少访问DOM和尽量减少标记。遍历大型DOM树会对脚本性能产生较大影响。

在多个函数都会取得一组类型元素的情况下,可以考虑将结果放在全局变量里共享,或把一组元素以参数形式传递给函数处理。

可以把多个脚本合并起来,减少加载页面时发送的请求数量。

位于 <head> 块中的脚本会导致浏览器无法并行加载其它文件,这些资源只有等脚本加载完毕后才会下载。

在加载脚本时,window 对象的 load 事件依然可以执行对文档进行的各种操作。

还可以压缩脚本文件:把脚本文件中不必要的字节,如注释和空白符都删去。这点可以待脚本编写完毕后借助工具实现。多数情况下,应该准备两个脚本,一个用于编写,一个用于部署。部署的脚本名应该加上 min 字样,如 style.min.js

改进脚本

有时候编写脚本需要对 window.load 事件添加很多内容,此时可以分成多个函数,再统一在匿名函数中调用。

可以使用以下函数来为该事件添加新的函数:

function addLoadEvent(func) {
    var oldonload = window.onload;
    if (typeof window.onload != "function") {
        window.onload = func;
    }
    else {
        window.onload = function() {
            oldonload();
            func();
        }
    }
}

如果该事件处理函数还没有绑定任何函数,就绑定为该函数;否则,把这个新函数追加到现有指令末尾。

JavaScript中也存在三元操作符 ?: ,其用法为:

condition ? true-expression : false-expression

可以使用结点对象的 nodeName 属性来查询结点元素名,该属性返回的是一个标签的大写字母值,例如 DIV

处理键盘访问

之前使用 onclick 事件处理鼠标点击,但是链接还可能被键盘按下访问。

可以使用 onkeypress 事件来处理键盘事件。按下任意键都会触发该事件。

可以如下整合键盘输入与鼠标点击事件:

links[i].onclick = function() {
    return showPicture(this) ? false : true;
}
links[i].onkeypress = links[i].onclick;

注意 onkeypress 事件,它很容易出问题,因为包括 Tab 键在内的任意按键都会触发它,可能会干扰一些按键行为。因此尽量不要使用该事件处理函数。

几乎所有的浏览器里,用 Tab 键移到某个链接再按下回车键的动作也会触发 onclick 事件。

HTML-DOM 提供了许多描述HTML元素的属性,例如,以下几组对象实际上是一样的:

/* equals */
document.getElementsByTagName("form");
document.forms;
/* equals */
element.getAttribute("src");
element.src

这些方法和属性可以相互替换。还可以直接为属性赋值来设置对应的值。