JavaScript(04): BOM和DOM

来源:互联网 发布:淘宝如何分销 编辑:程序博客网 时间:2024/04/29 09:21

先说明一个问题,到底是将JavaScript内嵌在HTML页面中还是做成外部文件?关于这一点并没有什么必须遵循的规则,但是通常大量的JavaScript代码不应该内嵌在HTML页面中,原因如下:

  • 安全性:如果每个人都可以随便查看页面的源代码,那么就有可能会发现安全漏洞从而危及整个站点或应用程序的安全。
  • 代码维护:如果每个页面都写自己的JavaScript,那么代码维护将是一场噩梦,而且也不利于对代码的重用。
  • 缓存:浏览器通常会根据特定的设置缓存所有的外部JavaScript文件,这意味着如果多个页面使用同一个文件,那么该文件只需要下载一次,这将节省时间和网络带宽。
另外一个大家可能会关注的问题是<script>标签到底放在<head>中还是<body>里面?一般情况下所有的代码和函数的定义都应该放在<head>标签中,这样在显示页面主体后代码已经完全被浏览器装载,可以直接调用了。我们不建议在页面<body>标签内直接调用JavaScript函数,建议<body>中对JavaScript函数的调用都是跟事件绑定在一起的。

1. BOM

探讨JavaScript就不能不说到BOM(浏览器对象模型),它提供了独立于内容而与浏览器窗口进行交互的对象。window对象表示整个浏览器窗口,通过它可以对浏览器进行各种操作,包括移动或调整浏览器窗口的大小等。

  • moveBy(dx, dy):将浏览器窗口水平移动dx个像素,垂直移动dy个像素。
  • moveTo(x, y):将浏览器窗口移到屏幕的(x, y)处。
  • resizeBy(dw, dh):将浏览器窗口宽度调整dw个像素,高度调整dh个像素。
  • resizeTo(w, h):将浏览器窗口宽度设置为w,高度设置为h,此方法不能使用负数作为参数。
如果想要获得窗口的宽度和高度以及在屏幕上的位置就存在浏览器兼容性问题,应为没有相关的标准。

  • IE提供了window.screenLeft和window.screenTop对象来判断窗口的位置,但是没有提供判断窗口大小的方法。通过document.body.offsetWidth和document.body.offsetHeight可以获得HTML页面区域的大小,这些都不是标准属性。
  • Mozilla、Opera和Safari提供了window.screenX和window.screenY属性判断窗口的位置。除此之外,还提供了window.innerWidth和window.innerHeight属性来判断页面区域的大小以及window.outerWidth和window.outerHeight来判断浏览器窗口的大小。
温馨提示:专业的Web站点通常都不会做移动窗口或者调整窗口大小这种无用的效果,所以即使这些效果感觉上很酷但仍然要避免使用它们。

通过window.open()方法可以打开新窗口,但是由于这种弹出式的窗口通常都被认为是令人厌恶的广告,于是乎很多浏览器都提供了拦截弹出窗口的功能,因此关于此方法不做过多的解释。

window对象的alert()、confirm()和prompt()方法可以产生系统对话框,分别警告框、确认框和输入框,而且都是模态(modal)对话框。

通过window对象的status属性和defaultStatus属性可以操作浏览器窗口的状态栏,但是专业的Web站点通常也不会用JavaScript去操作状态栏,因为它会分散用户的注意力而且不产生实用价值,虽然很多业余人士喜欢在状态栏制作一些跑马灯效果,但是很明显这种效果不仅没用还很让人讨厌,极其的不专业。最主要的是,很多浏览器已经没有状态栏了,比如FireFox和Chrome。

对于熟悉Java的开发者来说,可以使用对象的wait()方法使得程序暂停或者等待指定的时间后才继续执行。这种功能经常都会用到,然后JavaScript中并没有相应的支持。在JavaScript中可以通过window对象的setTimeout()方法设置暂停,或者说指定在将来的某个时间执行某项操作,可以通过clearTimeout()清除暂停。除此之外,window对象还提供了setInterval()方法可以指定某项操作按照指定的时间间隔周期性的执行,同样也可以通过clearInterval()将其取消。

window对象的history属性代表了访问浏览器窗口的历史。该对象提供了forward()、back()和go()方法实现历史页面的前进和后退功能。

我们最熟悉的document对象实际上也是window对象的属性,document对象有很多的属性和方法,这些可以查询参考手册获得相关信息,其中write()和writeln()方法可以在页面中输出内容,但是需要注意的是:必须在完全载入页面前调用write()和writeln()方法,如果这两个方法中的任何一个在页面载入后被调用,那么整个页面的内容会被抹去然后再输出方法中指定的内容。

window对象的location属性表示载入窗口的URL(简单说就是地址栏),通过该对象的href属性可以修改地址栏的内容,还可以通过该对象的reload()方法重新载入页面。通常应该将reload()方法放在代码的最后一行,因为reload()之后的代码可能执行也可能不执行(与网络延迟和资源加载等因素相关),放在前面可能会导致不确定行为。这里可以展示一个经常使用的效果,页面的延迟跳转

<!DOCTYPE html><html><head><title></title><script type="text/javascript">var counter = 5;var delayGoToURL = function () {document.getElementById("counter").innerHTML = counter;counter--;if(counter == 0) {window.location.href = "http://www.baidu.com";delayGoToURL = function() {};}setTimeout("delayGoToURL()", 1000);}window.onload = delayGoToURL;</script></head><body><h1><span id="counter"></span>秒之后跳转到百度</h1></body></html>

其中第13行重新绑定delayGoToURL函数是为了防止页面不能及时跳转时,计数器会变成0甚至负数的情况。

window对象的navigator属性提供了对浏览器信息的封装,它的appCodeName、appName等属性在对浏览器进行判断和检测时有重要作用,很多时候浏览器兼容性检测都需要用到navigator对象。

可以通过window对象的screen属性获取关于用户屏幕的相关信息:

  • availHeight:窗口可以使用的屏幕高度(以像素计)
  • availWidth:窗口可以使用的屏幕宽度(以像素计)
  • colorDepth:颜色位数
  • height:屏幕的高度(以像素计,即纵向分辨率)
  • width:屏幕的宽度(以像素计,即横向分辨率
2. DOM

DOM(文档对象模型)是将整个HTML页面视为一个树形结构,那么DOM树的节点(node)层次是这样的:

  • Document---最顶层节点,所有其他节点都是它的子节点
  • DocumentType---DTD引用
  • DocumentFragment
  • Element---表示起始标签和结束标签之间的内容
  • Attr---代表一个键值对(不能包含子节点)
  • Text---代表XML文档中在其实标签和结束标签之间的普通文本(不能包含子节点)
  • CDataSection
  • Entity
  • ProcessingInstruction
  • Comment
  • Notation
DOM定义了Node接口,其中包含了大量的属性和方法对提供对页面元素的操作:

  • nodeName --- String --- 节点的名字
  • nodeValue --- String ---节点的值
  • nodeType --- Number --- 节点类型对应的常量值
    • 1 --- Node.ELEMENT_NODE
    • 2 --- Node.ATTRIBUTE_NODE
    • 3 --- Node.TEXT_NODE
    • 9 --- Node.DOCUMENT_NODE
  • ownerDocument --- Document --- 指向节点所属的文档
  • firstChild --- Node --- 子节点中的第一个节点
  • lastChild --- Node --- 子节点中的最后一个节点
  • childNodes --- NodeList --- 所有子节点列表
  • previousSibling --- Node --- 前一个兄弟节点
  • nextSibling --- Node --- 后一个兄弟节点
  • hasChildNodes() --- Boolean --- 是否包含一个或多个子节点
  • attributes --- NamedNodeMap --- 元素的属性
  • appendChild(node) --- Node --- 追加子节点
  • removeChild(node) --- Node --- 删除子节点
  • replaceChild(newnode, oldnode) --- Node --- 替换子节点
  • insertBefore(newnode, refnode) --- Node --- 插入子节点

要访问<html />元素,可以使用document.documentElement属性(在IE 5.5中存在错误),例如:

var html = document.documentElement;var head = html.firstChild;    // <head />var body = html.lastChild;    // <body />alert(body.nodeName);        // BODYalert(body.nodeType);        // 1 --- Node.ELEMENT_NODE


当然,也可以通过document.body访问<body />元素。

Element节点的attributes属性其实是NamedNodeMap,提供了用于访问和处理其内容的方法:

  • getNamedItem(name):返回nodeName属性值等于name的节点。
  • removeNamedItem(name):删除nodeName属性值等于name的节点。
  • setNamedItem(node):将node添加到列表中,按其nodeName属性进行索引。
  • item(pos):像NodeList一样,返回位置在pos的节点。
<!DOCTYPE html><html><head><title></title></head><body><p style="color:red" id="test">Hello</p><script type="text/javascript">var p = document.getElementsByTagName("p")[0];alert(p.attributes.getNamedItem("id").value); // test</script></body></html>

如果要访问指定节点,可以通过以下方法实现:

  • getElementsByTagName():此方法当参数为”*“时可以返回所有元素,但是在低版本的IE(IE6-)中不被支持(可以通过document.all替代)。
  • getElementsByName():这个方法通过名字取到一组元素,例如在制作复选框效果(全选、反选)的时候该方法就特别有用。
<!DOCTYPE html><html>  <head>    <title>01.html</title>    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">    <meta http-equiv="description" content="this is my page">    <meta http-equiv="content-type" content="text/html; charset=UTF-8">        <!--<link rel="stylesheet" type="text/css" href="./styles.css">--><script type="text/javascript">function selectAllOrNot(flag) {var x = document.getElementsByName("goodsName");for(var i = 0; i < x. length; i++) {x[i].checked = flag;}}function reverseSelect() {var x = document.getElementsByName("goodsName");for(var i = 0; i < x. length; i++) {x[i].checked = !x[i].checked;}}</script>  </head>    <body>  <a href="javascript:selectAllOrNot(true)">全选</a>  <a href="javascript:reverseSelect()">反选</a>  <a href="javascript:selectAllOrNot(false)">取消</a>  <table>  <tr>  <td style="width:25px"></td>  <td>商品名称</td>  <td>商品价格</td>  </tr>  <tr>  <td><input type="checkbox" name="goodsName"/></td>  <td>篮球</td>  <td>98.5</td>  </tr>  <tr>  <td><input type="checkbox" name="goodsName"/></td>  <td>矿泉水</td>  <td>1.5</td>  </tr>  <tr>  <td><input type="checkbox" name="goodsName"/></td>  <td>方便面</td>  <td>2.3</td>  </tr>  <tr>  <td><input type="checkbox" name="goodsName"/></td>  <td>自行车</td>  <td>4500</td>  </tr>  </table>  </body></html>

  • getElementById():不解释,但是在IE 6中存在bug,使用低版本IE时需要小心。
要创建和删除节点,可以通过下面的方法完成:

  • createElement() / createTextNode() / appendChild()
<!DOCTYPE html><html><head><title></title><script type="text/javascript">function createMessage() {var p = document.createElement("p");p.style.fontSize = "32px";p.style.fontFamily = "Times New Roman";p.style.fontWeight = "bolder";var text = document.createTextNode("Hello, world!");p.appendChild(text);document.body.appendChild(p);}</script></head><body onload="createMessage()"></body></html>

  • removeChild() / replaceChild() / insertBefore()
<!DOCTYPE html><html><head><title></title><script type="text/javascript">function replaceMessage() {var op = document.getElementsByTagName("p")[0];var np = document.createElement("p");var style = document.defaultView.getComputedStyle(op, null);np.style.fontSize = style.fontSize;np.style.fontFamily = style.fontFamily;var text = document.createTextNode("Goodbye, world!");np.appendChild(text);op.parentNode.replaceChild(np, op);}</script></head><body onload="replaceMessage()"><p style="font-size:32px;font-family:Times New Roman">Hello, world!</p></body></html>

[重要提示]:所有的DOM操作必须在页面完全载入之后才能进行。当页面正在载入时,要向DOM插入相关代码是不可能的,因为在页面完全下载到客户端浏览器之前是无法构建完整的DOM树的。为此最好使用onload事件句柄来执行DOM操作的代码。

Web页面开发时经常会用到对表格的处理操作,操作表格可以使用下面的属性和方法:

  • caption:表标题<caption />
  • tBodies:表身<tbody />
  • tFoot:表尾<tfoot />
  • tHead:表头<thead />
  • rows:表格中所有的行
  • createTHead() / createTFoot() / createCaption() / deleteTHead() / deleteTFoot() / deleteCaption()
  • deleteRow(pos):删除表格中指定位置的行
  • insertRow(pos):在表格指定位置插入新行
<tr />元素的属性和方法:
  • cells:表格中的所有单元格
  • deleteCell(pos):删除行中指定位置的单元格
  • insertCell(pos):在行的指定位置插入单元格
一个通过DOM操作表格的例子:

<!DOCTYPE html><html>  <head>    <title>01.html</title>    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">    <meta http-equiv="description" content="this is my page">    <meta http-equiv="content-type" content="text/html; charset=UTF-8">    <style type="text/css">    td {    width: 75px;    }    </style>    <!--<link rel="stylesheet" type="text/css" href="./styles.css">--><script type="text/javascript">function $(id) {return document.getElementById(id);}function init() {var myRows = $("carts").rows;for(var i = 1; i < myRows.length - 1; i++) {myRows[i].id = "row" + i;myRows[i].cells[4].innerHTML = "<input type=\"button\" value=\"删除\" onclick=\"deleteRow('" + myRows[i].id + "')\" />";myRows[i].cells[5].innerHTML = "<input type=\"button\" value=\"修改\" onclick=\"editRow('" + myRows[i].id + "')\" />";}calcTotal();}function calcItem() {var myRows = $("carts").rows;for(var i = 1; i < myRows.length - 1; i++) {var total = myRows[i].cells[1].innerHTML * myRows[i].cells[2].innerHTML;var totalDisplay = Math.round(total * 10) / 10;myRows[i].cells[3].innerHTML = totalDisplay;}calcTotal();}function calcTotal() {var myRows = $("carts").rows;var sum = 0;for(var i = 1; i < myRows.length - 1; i++) {var totalDisplay = myRows[i].cells[3].innerHTML;sum += parseFloat(totalDisplay);}var lastRow = myRows[myRows.length - 1];lastRow.cells[1].innerHTML = "¥" + sum;}function deleteRow(rowId) {$("carts").deleteRow($(rowId).rowIndex);calcTotal();}function editRow(rowId) {var cell = $(rowId).cells[2];cell.innerHTML ="<input type='text' size='4' value='" + cell.innerHTML + "'/>";var button = $(rowId).cells[5].firstChild;button.setAttribute("value", "保存");button.setAttribute("onclick", "saveRow('" + rowId + "')");}var reg = /^\d+$/;function saveRow(rowId) {var cell = $(rowId).cells[2];if(reg.test(cell.firstChild.value)) {cell.innerHTML = cell.firstChild.value;var button = $(rowId).cells[5].firstChild;button.setAttribute("value", "修改");button.setAttribute("onclick", "editRow('" + rowId + "')");calcItem();}else {alert("请输入正整数");}}window.onload = function() {init();calcItem();};</script>  </head>    <body>      <div align="center">    <table id="carts" style="border-style:solid; border-left-color: black">  <tr>  <td>商品名称</td>  <td>商品价格</td>  <td>商品数量</td>  <td align="right">小结</td>  <td colspan="2"></td>  </tr>  <tr>  <td>篮球</td>  <td>98.5</td>  <td onclick="foo(this)">3</td>  <td align="right"></td>  <td></td>  <td></td>  </tr>  <tr>  <td>矿泉水</td>  <td>1.5</td>  <td>10</td>  <td align="right"></td>  <td></td>  <td></td>  </tr>  <tr>  <td>方便面</td>  <td>2.3</td>  <td>12</td>  <td align="right"></td>  <td></td>  <td></td>  </tr>  <tr>  <td>自行车</td>  <td>4500</td>  <td>2</td>  <td align="right"></td>  <td></td>  <td></td>  </tr>  <tr>  <td width="100">总计</td>  <td colspan="3" align="right"></td>  <td colspan="2"></td>  </tr>  </table>    </div>  </body></html>

遍历所有节点的方法

DOM Level 2中提供了遍历DOM的接口(至于你的浏览器到底支持哪种级别的DOM,这个需要去查一下,反正IE支持的DOM级别应该是很低的),下面通过一个例子简单的说明一下

<!DOCTYPE html><html><head><title></title><script type="text/javascript">function foo() {var walker = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT, null, false);var oNode = walker.nextNode();var output = "";while(oNode) {output += oNode.tagName + "\n";oNode = walker.nextNode();}alert(output);}window.onload = foo;</script></head><body><h1>Hello, world!</h1></body></html>

在Chrome和FireFox中运行的结果为: