下班已经很晚了,还是决定啃会儿,回顾回顾基础的知识点儿~
进入正题咯~

第9章 客户端检测

检测web客户端的手段很多,而且各有利弊。但最重要的还是要知道,不到万不得已,就不要使用客户端检测,只要能找到更通用的方法,就应该优先采用更通用的方法。
一言以蔽之,先设计最通用的方案,然后再使用特定于浏览器的技术增强该方案。

能力检测

function getElement(id){
    if(document.getElementById){
        return document.getElementById(id);
    }else if (document.all){
        return document.all[id];
    }else {
        throw new Error("No way to retrieve element!");
    }
}

要理解能力检测,首先必须理解两个重要概念:
第一个就是先检测达成目的的最常用的特性,可以保证代码最优化,因为在多数情况下都可以避免测试多个条件。
第二个重要的概念就是必须测试实际要用到的特性。一个特性存在,不一定意味着另一个特性也存在。

在浏览器环境下测试任何对象的某个特性是否存在,可以使用下面函数。

function isHostMethod(object,property){
    var t = typeof object[property];
    return t == 'function' || (!!(t == 'object' && object[property])) || t == 'unknow';
}
result = isHostMethod(xhr, "open");   //true
result = isHostMethod(xhr, "foo");   //false

第10章 DOM

1、节点层次—Node类型
Javascript中的所有节点类型都继承自Node 类型,因此所有节点类型都共享着相同的基本属性和方法。
每个节点都有一个nodeType属性,勇于表明节点的类型。
想要确定一个节点是不是element类型,下面方法:

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

要了解节点的具体信息,可以用nodeName和nodeValue这两个属性。nodeName中保存的始终都是元素的标签名,而nodeValue的值始终是null。
每个节点都有一个childNodes属性,其中保存着一个NodeList对象。
下面栗子小姐姐为大家展示如何访问保存在NodeList中的节点

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

下面方法可以将NodeList对象转换为数组。来栗子小姐姐吧

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;
}

parentNode属性:指向文档数的父节点;包含在childNodes列表中的所有节点都具有相同的父节点,它们的parentNode指向同一个节点。
previousSibling和nextSibling属性,可以访问同一个列表中的其他节点。
firstChild和lastChild属性,分别指向childNodes列表中的第一个和最后一个节点。
下图形象地展示了上述关系:
image.png
hasChildNodes()方法在包含一个或多个字节点的情况下返回true。
ownerDocument属性,指向表示整个文档的文档节点。文档节点是每个文档的根节点。每个文档智能由一个文档元素。在HTML页面中,文档元素适中都是<html>元素。

操作节点的常用方法:appendChild()方法,用于向childNodes列表的末尾添加一个节点。更新完成后,appendChild()返回新增的节点。

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

insertBefore()方法,把节点放在childNodes列表中特定的位置上,而不是放在末尾。
接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点,同时被方法返回。

//插入后成为最后一个子节点
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

replaceChild()方法,要替换的节点将由这个方法返回并从文档树中移除,同时由要插入的节点占据其位置。
接受两个参数:要插入的节点和要替换的节点。

//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);

如果只想移除而非替换节点,就可以使用removeChild()方法。这个方法接受一个参数,即要移除的节点,被移除的节点将成为方法的返回值。

//移除第一个子节点
var formerFirstChild = someNode.removeChild(someNode.firstChild);

replaceChild()方法和removeChild()方法一样,移除的节点仍然为文档所有,只不过在文档中没有了自己的位置。

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

cloneNode()方法不会复制添加到DOM节点中的JavaScript属性,例如事件处理程序。这个方法只复制特性、子节点,其他一切都不会复制。
normallize()方法,唯一的作用就是处理文档树中的文档节点。

2、Document类型
documentElement属性,该属性始终指向HTML页面中的<html>元素。

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

var body = document.body;              //取得对<body>的引用
var originalTitle = document.title;     //取得文档标题
document.title = “new page title”;     //设置文档标题

URL属性中包含页面完整的URL(即地址栏中显示的URL)。
domain属性中只包含页面的域名。
referrer属性中则保存着链接到当前页面的那个页面的URL。

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

Document类型为取得元素的操作提供了两个方法:getElementById()和getElementsByTagName().
如果页面中多个元素的ID值相同,getElementById()只返回文档中第一次出现的元素。
var images = document.getElementsByTagName(“img”);
这行代码会将HTMLCollection对象保存在images变量中。与NodeList对象类似,可以使用方括号语法或item()方法来访问HTMLCollection对象的项

alert(images.length);     //输出图像的数量
alert(images[0].src);      //输出第一个图像元素的src特性
alert(images.item(0).src);    //输出第一个图像元素的src特性

HTMLCollection对象还有一个方法,叫做namedItem(),使用这个方法可以通过元素的name特性取得集合中的项。假如上面提到的页面包含如下<img>元素:
<img src=“myimage.gif” name=“myImage”>
那么就可以用如下方式取<img>元素:
var myImage = images.namedItem(“myImage”);

var allElements = document.getElementsByTagName(“*”);
//返回HTMLCollection中,包含了整个页面中的所有元素。

getElementsByName()方法,返回带有给定name特性的所有元素。
最常使用getElementsByName()方法
的情况是取得单选按钮;为了确保发送给浏览器的值正确无误,所有单选按钮必须具有相同的name特性。

var radios = document.getElementsByName(“color”);
//与document.getElementsByTagName()类似,getElementsByName()方法也会返回一个HTMLCollection。

有一个document对象的功能已经存在很多年了,那就是将输出流写入到网页中的能力。
这个能力体现在4个方法中:write()、writeIn()、open()和close()。
write()和writeIn()方法接受一个字符串参数,即要写入到输出流中的文本。write()会原样写入,而writeIn()则会在字符串的末尾添加一个换行符(n)。
在页面被加载的过程中,可以使用这两个方法向页面中动态地加入内容。

<html>
<head>
 <title>document.write() Example 2</title>
</head>
<body>
 <script type=“text/javascript”>
  document.write(“<script type=\”text/javascript\” src=\”file.js\”>” + “</script>”);   //“</script>”会被解释为与外部的</script>标签匹配,结果紧跟其后的”);将会出现在页面中
  document.write(“<script type=\”text/javascript\” src=\”file.js\”>” + “<\/script>”);   //字符串“<\/script>”不会被当作外部<script>标签的关闭标签。
 </script>
</body>
</html>

方法open()和close()分别用于打开和关闭网页的输出流。

3、Element类型。
要访问元素的标签名,可以使用nodeName属性,也可以使用tagName属性;这两属性会返回相同的值。

var div = document.getElementById("myDiv");
alert(div.tagName);          //"DIV"  在HTML中,标签名始终都以全部大写表示
alert(div.tagName == div.nodeName);      //true

HTMLElement类型直接继承自Element并添加了一些属性。添加的这些属性分别对应于每个HTML元素中都存在的下列标准特性。

<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>
var div = document.getElementById("myDiv");
alert(div.id);               //"myDiv"
alert(div.className);               //"bd"  注意这里是className,不是class,是因为class是ECMAScript的保留字
alert(div.title);               //"body text"
alert(div.lang);               //"en"
alert(div.dir);               //"ltr"

操作特性的DOM方法主要有三个,分别是getAttribute()、setAttribute()和removeAttribute()。

<div id="myDiv" my_special_attribute="hello"></div>
var div= document.getElementById("myDiv");
alert(div.getAttribute("id"));       //"myDiv"
alert(div.getAttribute("class"));    //"bd"
//想要得到class特性值,应该传入"class"而不是"className",后者只有通过对象属性访问特性时才用。
var value = div.getAttribute("my_special_attribute");  //hello
//通过getAttribute()方法也可以取得自定义特性的值。

有两类特殊的特性,它们虽然有对应的属性名,但属性的值与通过getAttribute()返回的值并不相同。
第一类特性时style,用于通过CSS为元素指定样式。通过getAttribute()访问时,返回的style特性值中包含的是css文本,而通过属性来访问它则返回一个对象。
第二类与众不同的特性时onclick这样的事件处理程序。当在元素上使用时,onclick特性中包含的是Javascript代码,则会返回一个Javascript函数。
由于存在这些差别,在通过Javascript以编程方式操作DOM时,开发人员经常不适用getAttribute(),而是只使用对象的属性。只有在取得自定义特性值得情况下,才会使用getAttribute()方法。

与getAttribute()对应的方法是setAttribute()。接受两个参数,要设置的特性名和值。

div.setAttribute("id", "someOtherId");
div.setAttribute("class", "ft");
div.id = "someOtherId";  //因为所有特性都是属性,所以直接给属性赋值可以设置特性的值。
div.align = "left";

removeAttribute()方法,用于彻底删除元素的特性,调用这个方法不仅会清除特性的值,而且也会从元素中完全删除特性。
div.removeAttribute("class");

document.createElement()方法可以创建新元素。这个方法只接受一个参数,即要创建元素的标签名。
var div = document.createElement("div");
由于新元素尚未被添加到文档树中,因此设置这些特性不会影响浏览器的显示,要把新元素添加到文档树,可以使用appendChild()、insertBefore()或replaceChild()方法。
document.body.appendChild(div); //把新创建的元素添加到文档的<body>元素中。

4、Text类型
使用document.createTextNode()创建新文本节点。

var element = document.createElement("div");
element.className = "message";

var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);

document.body.appendChild(element);

DOM操作技术
动态脚本:创建动态脚本有两种方法,插入外部文件和直接插入JavaScript代码。分别是以下两种封装好的方法。

function loadScript(url){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url;
    document.body.appendChild(script);
}
loadScript("client.js");
function loadScriptString(code){
    var script = document.createElement("script");
    script.type = "text/javascript";
    try{
        script.appendChild(document.createTextNode(code));
    }catch (ex){
        script.text = code;
    }
    document.body.appendChild(script);
}
loadScriptString("function sayHi(){alert('hi');}");

动态样式

function loadStyle(url){
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.type = "text/css";
    link.href = url;
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(link);
}
loadStyle("styles.css");
function loadStyleString(css){
    var style = document.createElement("style");
    style.type = "text/css";
    try{
        style.appendChild(document.createTextNode(css));
    }catch (ex){
        style.styleSheet.cssText = css;
    }
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(style);
}
loadScriptString("body{background-color:red}");

小结:
DOM是语言中立的API,用于访问和操作HTML和XML文档。
DOM由各种节点构成,简要总结如下。

  • 最基本的节点类型是Node,用于抽象地表示文档中一个独立的部分;所有其他类型都继承自Node。
  • Document类型表示整个文档,是一组分层节点的根节点。在javascript中,document对象是document的一个实例,使用document对象,有很多种方式可以查询和取得节点。
  • Element节点表示文档中的所有HTML或XML元素,可以用来操作这些元素的内容和特性。
  • 另外还有一些节点类型,分别表示文本内容、注释、文档类型、CDATA区域和文档片段。

访问DOM的操作在多数情况下都很直观,不过在处理<script>和<style>元素时还是存在一些复杂性。由于这两个元素分别包含脚本和样式信息,因此浏览器通常会将它们与其他元素区别对待。
理解的DOM的关键,就是理解DOM对性能的影响。DOM操作往往是对Javascript程序中开销最大的部分,因而访问NodeList导致的问题为最多。NodeList对象都是"动态的",这就意味着每次访问NodeList对象,都会运行一次查询。有鉴于此,最好的办法就是尽量减少DOM操作。

今天就先更新到这里吧,未完待续,宝宝们


前端小饼干
1 声望4 粉丝