Javascript DOM 编程艺术(第二版)读书笔记——DOM基础
1.DOM是什么
D=document(文档)
O=object(对象)
M=Model(模型)
DOM又称节点树
一些术语:
parent(父) child(子) sibling(兄弟) node(节点)
element node(元素节点) text node(文本节点)
2.获取元素
1.getElementById(与typeof操作符)
//getElementById 与 typeof //typeof操作符可以告诉我们它的操作数是一个字符串、数值、函数、布尔值还是对象 //getElementById会返回一个对象,且该调用只能有一个参数(id参数,且必须放在单引号或双引号里) alert(typeof document.getElementById("test") );
注意,这里的Element是没有“s”结尾的!后面两个才有!
按照书上的测试我们只能得出“document.getElementById("test")”的类型是一个对象,但实际上可以更进一步探讨这是个什么对象(是document对象么?):
alert( document.getElementById("test-id") );
输出的结果是object HTMLDivElement,这明显不是document对象,至于这个对象和document有何不同,和DOM有什么关系,以后再说(查文档)。
同时我也注意到,将这个方法返回的对象赋值给一个变量,该变量的类型将和右值类型相同:
var test_id = document.getElementById("test-id"); alert( test_id ); //输出的结果也是object HTMLDivElement
另外,我发现文档中称HTMLDivElement为“接口”而不是对象!为什么呢?后面要时刻注意对象、接口是什么东西,参考文章有:1,2,3
2.getElementByTagName
//getElementsByTagName
//只接受标签名作为参数 //返回一个对象数组(数组),每个对象分别对应document对象中的一个tag元素;所以我们可以使用数组对象的属性,比如length alert( document.getElementsByTagName("li").length );
我得先看看这个返回的是什么:
alert( document.getElementsByTagName("p") ); //输出object HTMLCollection
3.getElementsByClassName
//getElementsByClassName
//以空格来同时指定多个类名——由于返回的只有一个特殊的对象,理所当然的,这里返回的是同时具有testClass、testClassTwo类的标签。
//只接受类名为参数 alert( document.getElementsByClassName("testClass testClassTwo").length );
同样的,我得先知道这个方法返回的是什么东西——这里返回的不是真正意义上的“数组”:
alert( document.getElementsByClassName("test-class") ); //输出的结果是HTMLCollection,这个对象不是Array(数组)而是一个特殊的对象数组,他也有“.length”这个方法,即返回集合中子元素的数目
标记一下,为什么不是返回数组而是返回一个新定义的对象数组?肯定有原因。
这里要注意,这个方法在IE8以下是无法使用的,我们必须借助“getElementsByTagName”来定义这个函数才能在老版本浏览器中使用,具体的方法见这篇文章,有些函数还看不懂,不知道这个实现的原理,以后要回头看懂它。P42
4.返回的类型是什么,是一个很重要的问题。
这段代码将正确地输出id为“test-id”下有多少个标签,之所以能这样,是因为“getElementById”返回的是一个document的实例,所以第二行可以对“test_id”使用“getElementsByTagName”这个方法;同样地,因为“getElementsByTagName”返回的是一个特殊的对象,这个对象有“length”这个方法,所以我们才能在alert里面正确地输出。
如果我们把第一行和第二行反过来用,比如“某些标签里面的id=test-id的标签有几个?”,就会出错了(这是个伪命题,直接找id就好了,不需要找“某些标签里面的id”)——首先就是“getElementsByTagName”返回的对象是没有“getElem....”这种方法的,其它的就更不用说了。
同样地还能这么用:
var test_id = document.getElementById("test-id"); var test_items = test_id.getElementsByClassName("test-class"); alert(test_items.length); //输出该id下有多少个“test-class”类的标签
要意识到,文档中的每一个元素都是一个对象,DOM对这些不同的对象都定义了不同的一整套的方法和属性;
getElementById返回一个对象,该对象对应文档里一个特定的元素节点。
getElementsByClassName、getElementsByTagName返回一个对象数组,它们分别对应文档里的一组特定的元素节点——因为你一次只能改一个元素,所以你如果想借这个操作修改这个数组里某个具体元素的值,你就必须加下标!比如getElementsByClassName("p")[1](这里只是强调下标,实际上不一定是这么写的,真正的写法见下面的例子)之类。
5.childNodes属性
前面分别是获取某个id的元素、获取某个标签、获取某个类的元素,现在这个是获取任何一个元素的所有子元素:
3.获取和设置属性
1.getAttribute(获取)
//getAttribute //为了方便,我只写body里的内容。 <div id="test-id"> <p title="oneTitle">hello word1</p> <h1 class="test-class"> <p>1</p> <p>2</p> </h1> <p title="twoTitle">hello word3</p> <p>hello word4</p> </div> <script> var test_items = document.getElementsByTagName("p"); for(var i = 0 ; i < test_items.length ; i++){ alert( test_items[i].getAttribute("title") ); } </script> //输出的结果依次为:oneTitle、null、null、twoTitle、null
学到这,我注意到:DOM分支下之所以有那么多的对象或者说是接口,其实都是为了针对不同抽象层级的操作而诞生的。比如,文档树里的各个节点的层级,我们操作的目标往往是各个节点的属性。因为不需要获取这个节点本身,所以这里的getAttribute不是document对象的一个方法——它只专注于实现属性相关的操作,所以被独立出来,所以我们这里可以看到,要想调用getAttribute,不能通过document对象调用,而是和其“联用”,即先获取到你想更改的标签的数组(第一行代码),然后再调用节点抽象层级的对象的方法getAttribute(第二行script代码——顺带一说,这里的节点抽象层级的对象其实就是指前面提到的HTMLCollection)。
上面一段的可以简单概括为:getAttribute方法是属于HTMLCollection对象的,而HTMLCollection对象是由document对象中的getElementsByTagName等方法返回的结果。
其次,要注意到上面这段代码的输出中有“null”,在Javascript里,null的意思是“没有值”。(个别浏览器会在输出null对话框的时候输出空白对话框)
2.setAttribute(设置)
//setAttribute //为了方便,只给出body内的代码 <div id="test-id"> <p title="oneTitle">hello word1</p> <h1 class="test-class"> <p>1</p> <p>2</p> </h1> <p title="twoTitle">hello word3</p> <p>hello word4</p> </div> <script> var test_id = document.getElementById("test-id"); alert( test_id.getAttribute("title") ); test_id.setAttribute("title","test-title"); //测试是否修改了 alert( test_id.getAttribute("title") );
</script>
setAttribute实际操作是这样的:
1.当对象属性值为null时(可能有点奇怪,但就是这样:属性没设置就相当于属性不存在,而属性不存在就是属性=null),先创建这个属性,然后设置它的值。
2.当属性的值存在时,覆盖掉它。
通过setAttribute对文档做出修改后,浏览器的开发者模式看到的源代码中属性的值并没有改变——其做出的修改不会反映在文档本身的源代码里,是“表里不一”的,这种思想源自DOM的工作模式:先加载文档的静态内容,再动态刷新,动态刷新不影响文档的静态内容。这也是DOM的真正威力:对页面内容进行刷新却不需要在浏览器里刷新页面。——————仔细想想,这是一连串的影响造成的。浏览器解析步骤的开始就是下载HTML文档,生成文档树,然后再渲染CSS、JS,如果JS改了HTML文档,那么浏览器就需要从头开始,也就是整个页面都要重新渲染一次,而这个代价出现的原因居然是修改某个属性值?所以干脆变成“动态刷新”。