(十五)

作者: xpwei | 来源:发表于2018-04-14 15:43 被阅读9次

DOM
DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序编程接口)。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。DOM脱胎于Netscape及微软公司创始的DHTML(动态HTML),但现在它已经成为表现和操作页面标记的真正的跨平台、语言中立的方式。

Node类型
DOM1级定义了一个Node接口,该接口将由DOM中的所有节点类型实现。这个Node接口在JavaScript中是作为Node类型实现的;除了IE之外,在其他所有浏览器中都可以访问到这个类型。JavaScript中的所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法。
每个节点都有一个nodeType属性,用于表明节点的类型。节点类型由在Node类型中定义的下列12个数值常量来表示,任何节点类型必居其一:

Node.ELEMENT_NODE(1)
Node.ATTRIBUTE_NODE(2)
Node.TEXT_NODE(3)
Node.CDATA_SECTION_NODE(4)
Node.ENTITY_REFERENCE_NODE(5)
Node.ENTITY_NODE(6)
Node.PROCESSING_INSTRUCTION_NODE(7)
Node.COMMENT_NODE(8)
Node.DOCUMENT_NODE(9)
Node.DOCUMENT_TYPE_NODE(10)
Node.DOCUMENT_FRAGMENT_NODE(11)
Node.NOTATION_NODE(12)。

通过比较上面这些常量,可以很容易地确定节点的类型

if (someNode.nodeType == Node.ELEMENT_NODE) { //在IE 中无效
    alert("Node is an element.");
}

这个例子比较了someNode.nodeType与Node.ELEMENT_NODE常量。如果二者相等,则意味着someNode确实是一个元素。然而,由于IE没有公开Node类型的构造函数,因此上面的代码在IE中会导致错误。为了确保跨浏览器兼容,最好还是将nodeType属性与数字值进行比较:

if (someNode.nodeType == 1) { //适用于所有浏览器
    alert("Node is an element.");
}

节点关系
每个节点都有一个childNodes属性,其中保存着一个NodeList对象。NodeList是一种类数组对象,用于保存一组有序的节点,可以通过为止来访问这些节点。请注意,虽然可以通过方括号语法来访问NodeList的值,而且这个对象也有length属性,但它并不是Array的实例。NodeList对象的独特之处在于,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构的变化能够自动反映在NodeList对象中。我们常说,NodeList是有声明、有呼吸的对象,而不是在我们第一次访问它们的某个瞬间拍摄下来的一张快照。
下面的例子展示了如何访问保存在NodeList中的节点——可以通过方括号,也可以使用item()方法:

var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;

在本书前面介绍过,对arguments对象使用Array.prototype.slice()方法可以将其转换为数组。而采用同样的方法,可以将NodeList对象转换为数组:

//在IE8 及之前版本中无效
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);

除IE8及更早版本之外,这行代码能在任何浏览器中运行。由于IE8及更早版本将NodeList实现为一个COM对象,而我们不能像使用JScript对象那样使用这种对象,因此上面的代码会导致错误:

function convertToArray(nodes) {
    var array = null;
    try {
        array = Array.prototype.slice.call(nodes, 0); //针对非IE浏览器
    } catch (ex) {
        array = new Array();
        for (var i = 0, len = nodes.length; i < len; i++) {
            array.push(nodes[i]);
        }
    }
    return array;
}

另外,hasChildNodes()也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回true。
所有节点都有的最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于两个或更多个文档中。通过这个属性,我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点。

操作节点

var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //true

如果在调用appendChild()时传入了父节点的第一个子节点,那么该节点就会成为父节点的最后一个子节点:

//someNode 有多个子节点
var returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild); //false
alert(returnedNode == someNode.lastChild); //true
//插入后成为最后一个子节点
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); //true
//插入后成为第一个子节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode); //true
alert(newNode == someNode.firstChild); //true
//插入到最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //true
//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
//替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
//移除第一个子节点
var formerFirstChild = someNode.removeChild(someNode.firstChild);
//移除最后一个子节点
var formerLastChild = someNode.removeChild(someNode.lastChild);

其他方法
有两个方法是所有类型的节点都有的。第一个就是cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本,cloneNode()方法接受一个布尔值参数,表示是否执行深复制。在参数为true的情况下,执行深复制,也就是复制节点及其整个子节点树;在参数为false的情况下,执行浅复制,即只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。因此,这个节点副本就称为了一个“孤儿”:

<ul>
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
</ul>
var deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); //3(IE < 9)或 7(其他浏览器)
var shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); //0

在这个例子中,deepList中保存着一个对myList执行深复制得到的副本。因此,deepList中包含3个列表项,每个列表项中都包含文本。而变量shallowList中保存着对myList执行浅复制得到的副本,因此它不包含子节点。deepList.childNodes.Length中的差异主要是因为IE8及更早版本与其他浏览器处理空白字符的方法不一样。IE9之前的版本不会为空白符创建节点。

cloneNode()方法不会复制添加到DOM节点中的JavaScript属性,例如事件处理程序等。这个方法只复制特性、(在明确指定的情况下也复制)子节点,其他一切都不会复制。IE在此存在一个bug,即它会复制事件处理程序,所以我们建议在复制之前最好先移除事件处理程序。

要介绍的最后一个方法是normalize(),这个方法唯一的作用就是处理文档数中的文本节点。由于解析器的实现或DOM操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点的情况。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点。

Document类型
JavaScript通过Document类型表示文档。在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。而且,document对象是window对象的一个属性,因此可以将其作为全局对象来访问。
文档的子节点

<html>
    <body>
    </body>
</html>
var html = document.documentElement; //取得对<html>的引用
alert(html === document.childNodes[0]); //true
alert(html === document.firstChild); //true

这个例子说明,documentElement、firstChild和childNode[0]的值相同,都指向<html>元素。
作为HTMLDocument的实例,document对象还有一个body属性,直接指向<body>元素。因为开发人员经常要使用这个元素,所以在JavaScript代码中出现的频率非常高:

var body = document.body; //取得对<body>的引用

所有浏览器都支持document.documentElement和document.body属性。
Document另一个可能的子节点是DocumentType。通常将<!DOCTYPE>标签看成一个与文档其他部分不同的实体,可以通过doctype属性(在浏览器中是document.doctype)来访问它的信息。

var doctype = document.doctype; //取得对<!DOCTYPE>的引用

由于浏览器对document.doctype的支持不一致,因此这个属性的用处很有限。
从技术上说,出现在<html>元素外部的注释应该算是文档的子节点。然而,不同的浏览器在是否解析这些注释以及能否正确处理它们等方面,也存在很大差异。所以,浏览器间的这种不一致也导致了位于<html>元素外部的注释没有什么用处。

文档信息

//取得文档标题
var originalTitle = document.title;
//设置文档标题
document.title = "New page title";

接下来的3个属性都与对网页的请求有关,所有这些信息都存在于请求的HTTP头部,只不过是通过这些属性让我们能够在JavaScript中访问它们而已:

//取得完整的URL
var url = document.URL;
//取得域名
var domain = document.domain;
//取得来源页面的URL
var referrer = document.referrer;

URL与domain属性是相互关联的。例如,如果document.URL等于http://www.wrox.com/WileyCDA/,那么document.domain就等于www.wrox.com
在这3个属性中,只有domain是可以设置的。但由于安全方面的限制,也并非可以给domain设置任何值。如果URL中包含一个子域名,例如p2p.wrox.com,那么就只能将domain设置为“wrox.com”。不能将这个属性设置为URL中不包含的域:

//假设页面来自 p2p.wrox.com 域
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错!

当页面中包含来自其他子域的框架或内嵌框架时,能够设置document.domain就非常方便了。由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信。而通过将每个页面的document.domain设置为相同的值,这些页面就可以相互访问对方包含的JavaScript对象了。例如,假设有一个页面加载自www.wrox.com,其中包含一个内嵌框架,框架内的页面加载自p2p.wrox.com。由于document.domain字符串不一样,内外两个页面之间无法相互访问对方的JavaScript对象。但如果这两个页面的document.domain值都设置为“wrox.com”,它们之间就可以通信了。
浏览器对domain属性还有一个限制,即如果域名一开始是“松散的”,那么不能将它再设置为“紧绷的":

//假设页面来自于 p2p.wrox.com 域
document.domain = "wrox.com"; //松散的(成功)
document.domain = "p2p.wrox.com"; //紧绷的(出错!)

所有浏览器都存在这个限制,但IE8是实现这一限制的最早的IE版本。

相关文章

  • 祛色斑秘方

    白芨十五克,白术十五克,白芷十五克,白茯苓十五克,白山药十五克,玫瑰花十五克,款冬花十五克白芍十五克,薏米十五克,...

  • 祛斑药方

    白芨十五克,白术十五克,白芷十五克,白茯苓十五克,白山药十五克,玫瑰花十五克,款冬花十五克白芍十五克,薏米十五克,...

  • 十五

    十五(1)林静宜真狠 十五(2)十五的称呼 十五(3)刚刚好 十五(5)想看看我吗? 十五(6)纯粹的事 十五(8...

  • 《十五为故乡》目录

    点此进入文集:十五为故乡 十五为故乡(1) 十五为故乡(2) 十五为故乡(3) 十五为故乡(4) 十五为故乡(5)...

  • 十五为故乡

    十五为故乡(1) 十五为故乡(2) 十五为了故乡生了气(3) 十五为故乡(4)

  • 2021-09-21

    十五的月亮,十五发

  • 共赢思维 | 刘氏文化传媒有限公司发布会(台州站)圆满成功!

    十五年前的梦想 十五年前的愿望 十五年前的目标 十五年的种种历练 十五年的运筹帷幄 十五年后的今天终于尘埃落定 刘...

  • 十五,十六

    十五吃月饼 十六胖 十五上山打老虎 十六下雨没带伞 十五没喜欢的人 十六没人喜欢 十五喝最烈的酒 十六不回头 十五...

  • 老马和小金

    一. 老马和小金是邻居。 老马今年三十五岁,小金今年也是三十五岁。 老马虽然三十五,长得却像四十五;小金虽然三十五...

  • 不出十五都是年,出了十五年过完

    不出十五都是年,出了十五年过完 文/孙新合 今天,农历正月十五,传统的元宵节。有道是,不出十五都是年,出了十五年...

网友评论

    本文标题:(十五)

    本文链接:https://www.haomeiwen.com/subject/nutlkftx.html