javascript --- javascript与DOM

时间:2021-11-24 19:17:00

javascript与DOM:

我们来个例子,一个HTML里包含一段文本和一个无序的列表。

<p id="intro">My first paragraph...</p>
<ul>
      <li>List item 1</li>
      <li>List item 1</li>
      <li>List item 1</li>
      <li>List item 1</li>
      <li>List item 1</li>
</ul>  

上面例子里,我们使用getElementById DOM方法来访问p段落。

var pDom = document.getElementById('intro'); // 现在有了该DOM节点,这个DOM节点展示的是该信息段落

变量pDom现在已经引用到该DOM节点上了,我们可以对该节点做很多事情,比如查询内容和属性,或者其它任何操作,甚至可以删除它,克隆它,或者将它移到到DOM树的其它节点上。

文档上的任何内容,我们都可以使用JavaScript和DOM API来访问,所以类似地,我们也可以访问上面的无序列表,唯一的问题是该元素没有ID属性,如果ID的话就可以使用相同的方式,或者使用如下getElementsByTagName方式:

var allU = document.getElementsByTagName('ul');  // 'getElementsByTagName'返回的是一个节点集合 - 和数组有些类似

getElementsByTagName

getElementsByTagName方法返回的是一个节点集合,和数组类似也有length属性,重要的一个特性是他是实时的——如果你在该元素里添加一个新的li元素,这个集合就会自动更新,介于他和数组类型,所以可以和访问数组一样的方法来访问,所以从0开始:

// 访问无序列表: [0]索引
var unorderedList = document.getElementsByTagName('ul')[0];
// 获取所有的li集合:
var allListItems = unorderedList.getElementsByTagName('li');
// 循环遍历
for (var i = 0, length = allListItems.length; i < length; i++) {
    // 弹出该节点的text内容
    alert(allListItems[i].firstChild.data);
} 

以下图例更清晰地展示了DOM获取的知识:

javascript --- javascript与DOM

DOM穿梭

“穿梭”这个词主要是用来描述通过DOM查找节点,DOM API提供了大量的节点属性让我们来往上或者往下查询节点。

所有的节点都有这些属性,都是可以用于访问相关的node节点:

  1. Node.childNodes: 访问一个单元素下所有的直接子节点元素,可以是一个可循环的类数组对象。该节点集合可以保护不同的类型的子节点(比如text节点或其他元素节点)。
  2. Node.firstChild: 与‘childNodes’数组的第一个项(‘Element.childNodes[0]‘)是同样的效果,仅仅是快捷方式。
  3. Node.lastChild: 与‘childNodes’数组的最后一个项(‘Element.childNodes[Element.childNodes.length-1]‘)是同样的效果,仅仅是快捷方式。shortcut.
  4. Node.parentNode: 访问当前节点的父节点,父节点只能有一个,祖节点可以用‘Node.parentNode.parentNode’的形式来访问。
  5. Node.nextSibling: 访问DOM树上与当前节点同级别的下一个节点。
  6. Node.previousSibling: 访问DOM树上与当前节点同级别的上一个节点。

javascript --- javascript与DOM

通过这张图,理解起来就简单多了,但有个非常重要的知识点:那就是元素之间不能有空格,如果ul和li之间有空格的话,就会被认为是内容为空的text node节点,这样ul.childNodes[0]就不是第一个li元素了。相应地,<p>的下一个节点也不是<ul>,因为<p>和<ul>之间有一个空行的节点,一般遇到这种情况需要遍历所有的子节点然后判断nodeType类型,1是元素,2是属性,3是text节点,详细的type类型可以通过此地址

    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

操作元素

每个DOM节点都包括一个属性集合,大多数的属性都提供为相应的功能提供了抽象。例如,如果有一个带有ID属性intro的文本元素,你可以很容易地通过DOM API来改变该元素的颜色:

document.getElementById('intro').style.color = '#FF0000';

为了理解这个API的功能,我们一步一步分开来看就非常容易理解了:

var myDocument = document;
var myIntro = myDocument.getElementById('intro');
var myIntroStyles = myIntro.style;  

// 现在,我们可以设置颜色了:
myIntroStyles.color = '#FF0000';

现在,我们有了该文本的style对象的引用了,所以我们可以添加其它的CSS样式:

myIntroStyles.padding = '2px 3px 0 3px';
myIntroStyles.backgroundColor = '#FFF';
myIntroStyles.marginTop = '20px'; 

这里我们只是要了基本的CSS属性名称,唯一区别是CSS属性的名称如果带有-的话,就需要去除,比如用marginTop代替margin-top。例如,下面的代码是不工作的,并且会抛出语法错误:

myIntroStyles.padding-top = '10em';
// 产生语法错误:
// 在JavaScript里横线-是减法操作符
// 而且也没有这样的属性名称

属性可以像数组一样访问,所以利用这个知识我们可以创建一个函数来改变任何给定元素的样式:

function changeStyle(elem, property, val) {
    elem.style[property] = val; // 使用[]来访问属性
}

// 使用上述的函数:
var myIntro = document.getElementById('intro'); // 获取intro文本对象
changeStyle(myIntro, 'color', 'red');  

通常DOM操作都是改变原始的内容,这里有几种方式来实现这个,最简单的是使用innerHTML属性,例如:

var myIntro = document.getElementById('intro');  

// 替换当前的内容
myIntro.innerHTML = 'New content for the <strong>amazing</strong> paragraph!';  

// 添加内容到当前的内容里
myIntro.innerHTML += '... some more content...';

唯一的问题是该方法没在规范里定义,而且在DOM规范里也没有定义,如果你不反感的话请继续使用,因为它比我们下面要讨论其它的方法快多了。

Node节点

var myIntro = document.getElementById('intro');  

// 添加内容
var someText = 'This is the text I want to add';
var textNode = document.createTextNode(someText);
myIntro.appendChild(textNode);

这里我们使用了appendChild方法将新text节点附件到文本字段,这样做比非标准的innerHTML方法显得有点长,但了解这些原理依然很重要,这里有一个使用DOM方法的更详细例子:

var myIntro = document.getElementById('intro');  

// 添加新连接到文本节点
// 首先,创建新连接元素
var myNewLink = document.createElement('a'); // <a/>
myNewLink.href = 'http://google.com'; // <a href="http://google.com"/>
myNewLink.appendChild(document.createTextNode('Visit Google'));
// <a href="http://google.com">Visit Google</a>  

// 将内容附件到文本节点
myIntro.appendChild(myNewLink);

另外DOM里还有一个insertBefore方法用于再节点前面附件内容,通过insertBefore和appendChild我们可以实现自己的insertAfter函数:

// 'Target'是DOM里已经存在的元素
// 'Bullet'是要插入的新元素

function insertAfter(target, bullet) {
    target.nextSibling ?
        target.parentNode.insertBefore(bullet, target.nextSibling)
        : target.parentNode.appendChild(bullet);
}  

// 使用了3目表达式:
// 格式:条件?条件为true时的表达式:条件为false时的表达式

上面的函数首先检查target元素的同级下一个节点是否存在,如果存在就在该节点前面添加bullet节点,如果不存在,就说明target是最后一个节点了,直接在后面append新节点就可以了。DOM API没有给提供insertAfter是因为真的没必要了——我们可以自己创建。

DOM操作有很多内容,上面你看到的只是其中一部分。

Event事件

浏览器事件是所有web程序的核心,通过这些事件我们定义将要发生的行为,如果在页面里有个按钮,那点击此按钮之前你需要验证表单是否合法,这时候就可以使用click事件,下面列出的最标准的事件列表:

注:正如我们上章所说的,DOM和JavaScript语言是2个单独的东西,浏览器事件是DOM API的一部分,而不是JavaScript的一部分。

鼠标事件

  1. mousedown’ – 鼠标设备按下一个元素的时候触发mousedown事件。
  2. mouseup’ – 鼠标设备从按下的元素上弹起的时候触发mouseup事件。
  3. click’ – 鼠标点击元素的时候触发click事件。
  4. dblclick’ – 鼠标双击元素的时候触发dblclick事件。
  5. mouseover’ – 鼠标移动到某元素上的时候触发mouseover事件。
  6. mouseout’ – 鼠标从某元素离开的时候触发mouseout事件。
  7. mousemove’ – 鼠标在某元素上移动但未离开的时候触发mousemove事件。

键盘事件

  1. keypress’ – 按键按下的时候触发该事件。
  2. keydown’ – 按键按下的时候触发该事件,并且在keypress事件之前。
  3. keyup’ – 按键松开的时候触发该事件,在keydown和keypress事件之后。

表单事件

  1. select’ – 文本字段(input, textarea等)的文本被选择的时候触发该事件。
  2. change’ – 控件失去input焦点的时候触发该事件(或者值被改变的时候)。
  3. submit’ – 表单提交的时候触发该事件。
  4. reset’ – 表单重置的时候触发该事件。
  5. focus’ – 元素获得焦点的时候触发该事件,通常来自鼠标设备或Tab导航。
  6. blur’ – 元素失去焦点的时候触发该事件,通常来自鼠标设备或Tab导航。

其它事件

  1. load’ – 页面加载完毕(包括内容、图片、frame、object)的时候触发该事件。
  2. resize’ – 页面大小改变的时候触发该事件(例如浏览器缩放)。
  3. scroll’ – 页面滚动的时候触发该事件。
  4. unload’ – 从页面或frame删除所有内容的时候触发该事件(例如离开一个页面)。