DOM(Document Object Model),文档对象模型(是什么,可以用来干什么)
1. DOM方法
1.1 getElementById
- 返回指定id的第一个对象
var div = document.getElementById("div");//返回id值为div的元素
1.2 getElementsByTagName
- 返回指定的标签名的一个类数组对象(因为返回的值是一个类数组,可以理解成多个对象,所以方法名中用Elements)
- 如果只要其中的某一个,可以用数组中的方括号来访问(起始下标为0)
- 想知道一共有几个,可以用length属性
var div = document.getElementsByTagName("div");//返回所有div元素组成的类数组对象
var div1 = document.getElementsByTagName("div")[0];//返回所有div元素中的第一个
var len = div.length;//返回div元素的数量(Number类型)
1.3 getAttribute
- 获取元素的指定的某一个属性
var mydiv = document.getElementById("div");
var iden = mydiv.getAttribute("id");//获得ID为div元素的ID属性值
1.4 setAttribute
- 设置元素的属性(包括了添加和修改,设置为空也可以理解成删除)
var mydiv = document.getElementById("div");
mydiv.setAttribute("class","mydiv");//把div元素的class属性值设置为mydiv
1.5 removeAttribute
- 删除元素的属性
var mydiv = document.getElementById("div");
mydiv.removeAttribute("class");//删除div元素的class属性,即为空
2. DOM属性
2.1 style
- 设置元素的CSS样式
var myDiv = document.getElementById("div");
div.style.background="blue";//ID值为div的元素的背景颜色被设置成为蓝色
2.2 innerHTML
- 返回元素标签内部的HTML(注意不一定是文档),可以理解成“读取”
- 设置元素标签内部的HTML,可理解成“写入”
var mydiv = document.getElementById("div");
var inner = mydiv.innerHTML;//获取ID为div的元素的内部的内容
//如果有内部嵌套标签则连同嵌套标签和文档一并返回
mydiv.innerHTML = "<p>Hello, world</p>"//内部套上p标签,文本内容为hello,world
2.3 className
- 返回元素的class属性值,可理解成“读取”
- 设置元素的class属性值,可理解成“写入”
var mydiv = document.getElementById("div");
mydiv.className;//返回ID为div元素的class属性,未设置时,控制台无输出
mydiv.className = "mydiv";//给div元素设置class属性为mydiv
3. DOM事件
事件,即文档/浏览器窗口发生的一些特定的交互瞬间
3.1 鼠标事件
3.1.1 onload
- 在页面加载完毕时触发
- 适用于script标签放在head部分时/针对文档内部标签相应的事件(必须在文档加载好之后才触发事件)
window.onload=function(){}//页面加载完毕后运行匿名函数function内部指令
3.1.2 onclick/onmouseover/onmouseout
- 鼠标点击时/移动到元素上方时/移出元素时触发
var div = document.getElementById("div");
function myalert1(){alert("你的鼠标放在一个div上面");}
var myAlert = function myalert2(){alert("你已经移出了div");}
div.onclick=function(){alert("你点击了一个div");}
div.onmouseover=myalert1();
div.onmouseout=myAlert;//鼠标移入后移出时触发,注意此处只要用变量名,不需要函数名
3.1.3 onmousedown/onmouseup/onmousemove
- 鼠标按下/松开/移动指针时触发
- onmousedown、onmouseup合在一起成就了onclick事件
- 发生顺序为:onmousedown、onmouseup、onclick
var div = document.getElementById("div");
div.onmousemove=function(){console.log("你在div里面移动了");}//在div里移动,控制台打印
div.onmousedown=function(){console.log("你点击了div");}
div.onmouseup=function(){console.log("你松开了div");}//点击和松开时控制台打印
3.1.4 onfocus/onblur
- 获得/失去焦点时触发
- 适用于input标签中type为text、password的以及textarea标签
var username = document.getElementById("username");
//当光标在username输入框,执行函数
username.onfocus=function(){p.innerHTML="请输入用户名!");}
//当光标离开username输入框,执行函数
username.onblur=function(){p.innerHTML="用户名输入正确!";)}
3.1.5 onchange
- 域的值发生变化时触发
- 主要适用于select下拉菜单、radio单选、checkbox多选
//根据选项调整页面的背景颜色
function changeBack(){
var div1 = document.getElementById("mydiv");
var menu = document.getElementById("changeColor");
menu.onchange=function(){
//可通过下拉菜单的options来创建类数组,通过selectedIndex访问选定的元素下标
var bg = menu.options[menu.selectedIndex].value;
div1.style.background=bg;
}
}
window.onload=changeBack;//注意事件触发时调用函数无需()
3.1.6 onsubmit
- 表单中确认按钮被点击时触发
- 该事件添加在表单元素上,而不是提交按钮上
<form action="post" id="test1">
<input type="text">
<input type="submit" value="提交" id="test2">
</form>
<script>
window.onload=myHere;
function myHere(){
var tijiao = document.getElementById("test1");
//注意此处onsubmit事件必须绑定在表单元素上才可以,绑定在提交按钮上无效
tijiao.onsubmit=function(){alert("谢谢提交");}
}
</script>
3.1.7 onresize/onscroll
- 当窗口大小发生变化/滚动条滚动时触发该事件
- 两个事件常常用在window对象上,但也可以用在div等块级元素上
3.2 键盘事件
3.2.1 onkeydown/onkeypress/onkeyup
- 在按下按键/按下键盘按键(字母、数字、符号、回车、空格)/松开按键时发生
- 发生顺序为onkeydown、onkeypress、onkeyup
- 可用keyCode属性返回按下键的编码
4. DOM操作
4.1 创建节点
4.1.1 createElement
- 创建元素节点
- 该方法创建的HTML5标签是可以兼容IE8以下的浏览器的
- IE6-8下可以创建HTML中不存在的标签
//创建一个li节点
var li = document.createElement("li");
4.1.2 createTextNode
- 创建文本节点
//创建一个text节点并把其添加到li节点中
var text = document.createTextNode("hello world");
li.appendChild(text);
4.1.3 createDocumentFragment
- 创建文档片段
- 创建目的:在DOM树中,文档片段被其所有的子元素所代替。 因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能
4.1.4 createComment
- 创建注释(极少用)
4.1.5 innerHTML
- 创建标签内部的HTML文档,可以添加文本也可以添加标签和属性,以字符串形式传入即可
- 读取标签内部的HTML文档,包括标签、属性、文本、注释等
- 限制:
- IE6-8中,传入的字符串左侧的空白会被自动删除
- 只有IE8及更早版本可以在script内部传入内容后执行script内部脚本,但script元素得有defer属性,且script前必须为有作用域元素(即不可为空)
- IE8及以前的浏览器
4.1.6 outerHTML
- 包括了父节点下的所有元素(包括自己)
4.1.7 innerText
- 只读取指定元素内部的文本,忽略其他(标签、属性、注释等)节点
- 写入时,会把标签一并当做文本写入
- IE4以上、Safari3以上、opera8以上、chrome支持,火狐不支持(有特有的textContent属性)
<div id="box">
我是div
<p>我是p标签</p>
<!--我是一个捣蛋的注释-->
<ul>
<li>li1</li>
<li>li2</li>
<li>li3</li>
</ul>
</div>
<script>
var box = document.getElementById('box');
console.log(box.innerText);//控制台只输出“我是div 我是p标签 li1 li2 li3”
box.innerText="<p>我是新的p标签</p>";//div中内容变成<p>我是新的p标签</p>
</script>
//对所有类型的浏览器通用的innerText方法
//读取版本
function getInnerText(element){
if(typeof element.textContent == "string"){
return element.textContent;
}
else{
return element.innerText;
}
}
//写入版本
function writeInnerText(element,text){
if(typeof element.textContent == "string"){
text = element.textContent;
}
else{
text = element.innerText;
}
}
4.1.8 outerText
- 读取时,同innerText功能一致
- 写入时,会把调用元素的子节点(包括子节点)全部替换掉
- 由于使用后会导致调用改属性的元素不再存在,不建议使用
4.2 遍历节点
4.2.1 DOM遍历的原理
- DOM文档也是一棵树,以document为根,包括了多种节点
4.2.2 父子节点之间的遍历
- 父节点到子节点:firstChild、lastChild、childNodes[i]、childNodes.item(i)
- 子节点到父节点:parentNode
- 子节点之间:nextSibling、previousSibling
4.2.3 其他方法
- 返回文档根节点:documentElement、ownerDocument(任意元素直接返回)
- 返回元素标签名:tagName
- 判断一个节点是否有子节点:hasChildNodes()
4.2.4 节点遍历的实现
//思路:判断当前节点是否有子节点,如果有则重复调用函数,遍历所有子节点,否则结束
var s = "";
//space为传入的连接符号,node为建树的起始节点
function travel(space,node){
if(node.tagName){
s += space + node.tagName + "<br/>";
}
var len = node.childNodes.length;
for(var i = 0;i < len;i++){
travel(space+"*",node.childNodes[i]);
}
}
travel("",document);
document.write(s);
4.2.5 节点遍历时的问题及方法(空白节点)
4.2.5.1 问题的原因
- childNode属性中,IE8以前的浏览器会忽略标签之间的空格
- 新浏览器会把这些空格当成节点
- 因此,遇到HTML.childNodes[1]时,浏览器识别出的不是body而是text
4.2.5.2 过滤掉空白的文本节点的方法
- 删除空白(不建议)
- 用tagname属性判断(不建议)
- 用nodeType属性(1为元素,2为属性,3为文本)
4.2.6 节点遍历的新方法(解决空白节点的问题)
- 父节点到子节点(从标签到标签):firstElementChild、lastElementChild、children[i]
- 子节点之间(从标签到标签):nextElementSibling、previousElementsSibling
- 返回所有子节点个数(只是子节点):childElementCount
4.3 类数组对象
4.3.1 Nodelist
- childNodes里保存着的对象
- 不是array实例,没有数组对象方法,只是保存一组有序节点
- 访问值可用方括号,方法和属性包括:
- item方法
- length属性
<div id="box">
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
</div>
<script>
var box = document.getElementById("box");
var nodes = box.childNodes;
console.log(nodes[1]);//得到box1这个div(空白节点也算入item里了)
console.log(nodes.item(1));//得到box1这个div(空白节点也算入item里了)
console.log(nodes.length);//结果为7(空白节点也算入length)
</script>
- 如果想把NodeList中内容转换成真的数组对象,可以用以下两种方法:
- Array对象的prototype属性下的slice方法(低版本IE运行时会报错)
- 元素循环遍历后输出
function makeArray(nodelist){
var arr = null;
try{
return Array.prototype.slice.call(nodelist);//把nodelist转换成数组
}
catch(e){
arr = new Array();
for(var i=0,len=nodelist.length;i<len;i++){
arr.push(nodelist[i]);
}
}
return arr;
}
4.3.2 HTMLCollection
- 表示HTML元素的集合
- 会返回HTMLCollection的常用属性和方法有:
- getElementsByTagName:返回指定标签的集合
- document.scripts:返回document里所有script元素集合
- document.links/images/forms:返回页面中所有a/图片/表单元素
- tr.cells:返回该表格的tr的所有td
- select.options:返回select的所有选项
- td.namedItem:在该表格元素集合中先找id为指定名的元素,若没有,找第一个name为指定名的元素,返回元素
- 方法和属性有:
- item方法
- length属性
- namedItem方法:在该元素集合下找id为指定名称的元素,若没有,找第一个name为指定名的元素,返回元素
<div id="box">
<div id="box1">我是div1</div>
<div name="box2">我是div2</div>
<div name="box2">我是div3</div>
</div>
<script>
var box = document.getElementsByTagName("div");
console.log(box.namedItem('box2'));//返回我是div2这个div
</script>
4.3.3 namedNodeMap
- 通过访问元素的attributes属性得到,返回元素节点特性的集合
- 常用方法和属性有:
- item:返回某一个元素节点特性
- length:返回元素节点特性数量
4.3.4 类数组对象的动态性
- Nodelist、HTMLColletcion、NamedNodeMap都是基于DOM结构动态查询的结果,DOM结构只要有变化,其也会相应发生变化
- 文档结构更新,三者都会更新,因此三者保存最新、最准确信息
4.4 节点查找
4.4.1 getElementById
- 返回id属性值为给定值的标签元素
- IE低版本浏览器中会把在id值为查找值之前name值为查找值的元素找出来
- 文本里设定两个id值相同的元素时,会查找到第一个
- 只可通过document对象来调用
<div name="box1">这个是错误的元素</div>
<div id="box1">这个才是正确的元素</div>
<div id="box1">这个是另一个正确的元素</div>
<button id="button1">点我</button>
<script>
function getElBId(){
var btn = document.getElementById("button1");
btn.onclick=function(){
var box = document.getElementById("box1");
console.log(box.innerHTML);//打印“这才是正确的元素”
}
}
window.onload=getElBId;//低版本IE下,会打印出“这个是错误的元素”
</script>
- 解决getElementById的bug(针对IE)的方法
- 避免在同一页面文档里出现同一名称的name和id(有时较难)
- 用document.all方法来解决
//思路:获取元素后判断该元素的id是否显示设置
//是的话,判断id是否为该值,如果不是该值,用document.all方法来获取文档中
//所有name和id为指定值的元素,一个个判断id值是否为给定值
var getElementById = function(id){
var el = document.getElementById(id);
//此处,\v在IE中被解析为v,其他浏览器中解析为垂直制表符(可理解成空格)
//\v1随后被转化为字符串v1/数字1,加号试图转化为数字(IE转化成NaN)
//取反后IE为true,其他为false
if(!+"\v1"){
if(el && el.id === id){return el;}
else{
var els = document.all[id],
n = els.length;
for(var i = 0;i < n;i++){
if(els[i].id === id){return els[i];}
}
}
}
return el;
}
4.4.2 getElementsByName
- 返回指定name属性值的一个类数组对象
- 只可通过document对象来调用
- 在IE6和Opera7.5上有bug
- 返回id为给定值的元素
- 仅对input和image元素起作用
4.4.3 getElementsByTagName
- 返回指定标签名的所有元素的一个类数组(传入值不区分大小写)
- 传入参数为!时,返回注释节点
- IE6-8下,文档类型声明也被当成了注释
- Chrome不支持该方法
- 传入参数为*时,返回页面上的所有元素
4.4.4 getElementsByClassName
- 返回指定class值的所有元素的一个类数组
- 传入多个class名时,用空格隔开
- Firefox3、Chrome、IE9、Safari4以上可以使用
- 解决兼容性问题的方法
//思路:(未完待续)
4.4.5 querySelector
- 以CSS选择器的方法来选择元素(.调用类,#调用id)
- 可以用任意元素来调用该方法(document或文档子节点)
- 找不到元素时,返回null
- 几乎所有主流浏览器都支持(IE8及以上支持)
4.4.6 querySelectorAll
- 返回一个查找的元素的集合staticNodelist(静态的类数组,数组第一次查找完后不会因为DOM的变化而实时更新)
- 当找不到元素时,返回一个空的集合
- 几乎所有主流浏览器都支持(IE8及以上支持)
4.5 节点操作
4.5.1 appendChild
- 在指定元素最后一个子节点后添加节点,返回新添加的节点
- 如果添加的是已经存在的子元素,则会把这个子元素移动到最后
<ul id="box1">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
var ul = document.getElementById('box1');
var cs = box1.firstElementChild;
ul.appendChild(cs);//ul的顺序改成2341
</script>
4.5.2 insertBefore
- 把指定节点放到指定的节点的前面
- 传入的第二个参数为null时,可以把节点添加到最后(相当于appendChild)
<ul id="box1">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.getElementById('box1');
var li = document.createElement('li');
var text = document.createTextNode('5');
li.appendChild(text);
var li2 = ul.children.item(1);
ul.insertBefore(li,li2);//在第二个li前面插入新的li元素
ul.insertBefore(li,null);//
</script>
4.5.3 replaceChild
- 替换一个子节点,返回这个替换的节点
ul.replaceChild(li,li2);//把ul里的li2替换成li
4.5.4 cloneNode
- 克隆一个节点,返回这个父本
- 传入参数true时,复制其中的子节点(默认为false)
<ul id="ul">
<li></li>
</ul>
<script>
var ul = document.getElementById('ul');
ul.cloneNode();//只拷贝ul这个节点,不包括内部的li
ul.cloneNode(true);//拷贝ul和内部的li
</script>
4.5.5 normalize
- 把文本节点合并
<div id="div">hello
world
</div>
<script>
var div = document.getElementById('div');
div.normalize();//div内部的文本会拼接成一个文本
</script>
4.5.6 splitText
- 按照指定的位置,把一个文本节点分割成两个
- 返回新的文本节点
<div id="div">helloworld
</div>
<script>
var div = document.getElementById('div');
div.splitText(5);//div内部第五个字符开始往后的成为一个新的字符串
</script>
4.6 删除节点
4.6.1 removeChild
- 删除某个节点中指定的子节点
- 传入的参数为删除的节点,且该方法必须传入参数
- 同innerHTML的“删除”比较:
- IE6-8,removeChild为掰断树枝,但树枝还可以继续用
- IE6-8,innerHTML为把树枝拔下来,烧掉
- Chrome浏览器,两者都只是掰断树枝
4.6.2 removeNode
- 把目标节点从文档中删除,返回删除的节点
- 参数为布尔值,默认为false(只删除目标节点,保留其下的子节点)
- 只在IE中起作用
5. DOM属性
5.1 属性介绍
- 固有属性property:浏览器捆绑好的属性
- 自定义属性attribute:自己设置的属性
- 同名的属性,以先定义的为准
- 属性名称全部为小写
- 无法通过元素后的属性访问,只能用getNamedItem()方法以及nodeValue属性访问属性值
- 不可通过console.log()方法输出,输出结果为undefined
5.2 方法和属性
5.2.1 getNamedItem()
- 该方法返回指定的属性节点
- 不能访问没有显式定义的属性,即便是固有属性也不可以
5.2.2 attributes
- 返回指定节点的属性集合
- 可通过.attributes[i].nodeValue访问某个节点第i个属性值
5.2.3 removeNamedItem()
- 删除一个属性,参数为待删除属性的名字
5.2.4 setNamedItem()
- 把创建好的属性添加到指定的元素上
var div = document.getElementById('div');
var test1 = document.createAttribute("xxx");
test1.nodeValue='hello';
div.attributes.setNamedItem(test1);//把test1这个属性值添加到div元素上
5.2.5 getAttribute()
- 操作固有属性和自定义属性的通用方法
- 获得指定属性名的属性值
- style、onclick属性用getAttribute和属性(div.style)获取时,返回值会不同
- 一般用JS来操作DOM时,不使用getAttribute方法,只有在取自定义属性的值时才使用getAttribute
5.2.6 setAttribute()
- 设置元素属性值,两个参数(属性名,属性值)
- IE7及更早版本不支持
5.2.7 removeAttribute()
- 删除属性
- 兼容IE6
5.3 布尔属性
- 传入的属性值参数会进行隐式的类型转换
- 常用的有:
- radio和checkbox中的checked
- option标签中的selected
- input的type值为text的readonly(设置属性时readOnly记得O大写)
- disabled
- select中的multiple
- hidden
5.4 data属性
- 用来存储页面/应用程序私有定义的属性
- 用dataset属性来获取data属性的值
- 一个-直接用名字(data-toggle)
- 多个-用驼峰式写名称(data-xxx-yyy变成dataXxxYyy)
5.5 classList
- IE11及以上才支持
- 方法:
- add:添加
- remove:移除
- has:判断是否存在
- toggle:存在就移除,不存在就添加