Js高级程序设计第三版学习(十章)
第十章 DOM
DOM是针对HTML和XML文档的一个API(只接触html很少接触xml),DOM描绘了一个节点树,允许开发人员,添加,修改,删除,页面的一部分.
1.节点层次:
节点分为几种不同的类型,每种节点都有各自的特性,方法,数据, 文档节点是每个文档的根节点, 文档元素(html) 是文档最外层的元素,每个文档只能有一个文档元素
HTML元素通过元素节点表示,特性通过特性节点表示,文档类型通过文档节点表示,所有节点类型都继承一个基类型
- Node类型
所有节点类型都继承自Node类型,Node类型定义了12个字符常量,每个节点都有一个nodetype属性,nodetype属性必定与Node类型中的一个字符常量所对应
if(ele.nodeType === Node.ELEMENT_NODE){
console.log('是一个元素节点') //是一个元素节点
}
if(ele.nodeType === 1){
console.log('是一个元素节点') //是一个元素节点
}
由于低版本ie不公开Node构造函数, 所以为了兼容书中建议我们比较数字, 每个节点类型都有对应的数字
每个节点都有一个childNodes属性, 这个属性保存了一个nodeList列表 该列表存放了子元素节点,nodeList列表是动态的,如果列表内部元素改变会反映到dom上,反之亦然. 虽然是list但是他并不是array的实例, 所以他只能被for循环迭代, 不能够被forEarch,for ..in,for... of迭代(会报错),
<ul id="uuu">
<li id="asd" class="ppap" data-nihao="nihao">1</li>
<li id="dsa">1</li>
<li>1</li>
<li id="asd" class="ppap" data-nihao="nihao">1</li>
</ul>
<script>
/*
要访问nodeList的节点 可以通过索引的形式,或者item()的形式都可以
length属性记录的节点个数,
*/
var ele = document.getElementById('uuu');
console.log(ele.childNodes.length);//9
console.log(ele.childNodes[0]);//#text
console.log(ele.childNodes.item(0))//#text
/*
会报错!!
for (const iterator of ele) {
}
ele.forEach(element => {
});
*/
</script>
值得注意的是childNodes中会保存空白的文本节点,而不仅仅是元素节点,这也是length等于9的原因, 每个节点都提供了一些属性用来访问关系节点,
/*
parentNode获取父节点 , nextSibling下一个节点 没有返回null previousSibling上一个节点 没有返回null
*/
var ele = document.getElementById('uuu');
var asd = document.getElementById('asd');
var firstChilddd = ele.childNodes[0];
var lastChilddd = ele.childNodes[ele.childNodes.length - 1];
console.log(firstChilddd.nextSibling);
console.log(firstChilddd.previousSibling); //null
console.log(firstChilddd.parentNode);
console.log(lastChilddd.nextSibling); //null
/*
如果要获取第一个节点或最后一个节点我们可以使用 firstChild或lastChild代替
*/
console.log(firstChilddd === ele.firstChild); //true
console.log(lastChilddd === ele.lastChild); //true
/*
hasChildNodes 用来查询是否包含XX节点
*/
console.log(ele.hasChildNodes(asd)); //true
对于节点的增删改..等等的方法
/*
parentNode获取父节点 , nextSibling下一个节点 没有返回null previousSibling上一个节点 没有返回null
*/
var ele = document.getElementById('uuu');
var asd = document.getElementById('asd');
var im = document.getElementById('im');
var firstChilddd = ele.childNodes[0];
var lastChilddd = ele.childNodes[ele.childNodes.length - 1];
// appendChild 在节点列表的末尾 添加节点
//如果该节点是页面中存在的节点,那么这个节点就被添加到目标节点
console.log(asd.appendChild(im)); //im
/*
insertBefore 插入节点, 如果第二参数为null 跟appendChild相同
不为null 则在目标节点插入
*/
console.log(ele.insertBefore(im, asd)); // im
/*
replaceChild 替换节点 第一个参数 替换节点 第二个参数 被替换的节点
返回被替换的节点
*/
console.log(ele.replaceChild(im, asd)); //asd
/*
removeChild 移除节点 返回被移除的节点,如果删除的是元素节点
元素节点中包含文本节点,其实文本节点不会被删除,虽然不会在页面上显示
但是其实依旧被父节点包含
*/
console.log(ele.removeChild(im));//im
/*
cloneAsd 克隆节点 参数为true为深克隆 连同子节点克隆
无参数 为潜克隆 只可知节点本身
*/
var asd = document.getElementById('asd');
var cloneAsd = asd.cloneNode();
console.log(cloneAsd);//浅
var cloneAsd2 = asd.cloneNode(true);
console.log(cloneAsd2);//深
/*
normalize() 会清除空白节点,
会把相邻的文本节点,变成一个文本节点.
*/
var asd = document.getElementById('asd');
var text1 = document.createTextNode('文本1');
var text2 = document.createTextNode('文本2');
var text3 = document.createTextNode('文本3');
asd.append(text1,text2,text3);
console.log(asd.childNodes);//NodeList(12)
asd.normalize();
console.log(asd.childNodes);//NodeList(9)
- Document类型
document对象是Document类型的实例, document代表整个html页面, document的子节点可以是 DocumentType(文档类型),Element(html节点),ProcessingInstroctior(不知道...),Comment(注释节点), document有两个快速访问子节点的属性, documentElement(html节点),documentBody(body节点)
如果存在文档声明(doctype) 那么doctype节点是第一个节点(ie11,chrome,ff),(与书上不同的是chrome不会把doctype加入nodeList中,但现在也会加入了), 如果存在注释,也会显示也会加入
document提供了一些网页信息的属性, tittle 文档标题 domain 域名 referrer 引用页的url URL 地址栏url
console.log(document.title); //文档标题
console.log(document.domain);// '' 域名
console.log(document.referrer);// '' 链接到当前页面的那个页面的url
console.log(document.URL);//地址栏的url
Dom1提供了三个方法来获取对象或对象集合, document.getElementById() 根据id查询, 返回单个对象,document.getElementsByTagName()根据标签名查找,返回一个动态集合HTMLCollection, 类似与nodeList,同样只能被for迭代,其他方式不行,同样可以通过item或索引的方式获取对象,但还有一个额外方法namedItem() 根据name值来获取对象,同时也支持按名称访问['xxxx'](但是只会获取第一个name为该字符串的对象),document.getElementsByName(''),根据name名查找,同样返回一个动态集合nodeList.
var asd = document.getElementsByTagName('asd');
console.log(asd);
var pList = document.getElementsByTagName('p');
console.log(pList); //HTMLCollection(4) [p, p, p, p, test: p]
console.log(pList['test']); // <p name="test">2</p>
console.log(pList.namedItem('test')); // <p name="test">2</p>
console.log(pList.item(0)); // <p name="test">2</p>
console.log(pList[0]);// <p name="test">2</p>
var testList = document.getElementsByName('test')
console.log(testList) //NodeList(3) [p, p, p]
//获取所有带name的a元素
console.log(document.anchors)
// 获取所有图片元素
console.log(document.images)
// 获取所有所有a元素
console.log(document.links)
//获取所有form元素
console.log(document.forms)
document.wirte(), 页面写入, 如果在页面加载中使用会在文档末尾添加字段, 如果在页面加载之后使用会重写整个页面(正常也不会使用)
- Element类型
Element用于表现html或xml的元素 , 元素名称可以用 tagName或nodeName获取
var asd = document.getElementById('asd');
console.log(asd.tagName)//LI
所有html元素都继承自HTMLElement类型,主要有id title className 这几个默认属性, 我们直接用属性的方式获取,也可以用getAttribute(),setAttribute();removeAttribute(),对属性进行增删改查
//获取元素
var asd = document.getElementById('asd');
//元素的标签名
console.log(asd.tagName); //LI
//元素的class
console.log(asd.className);//ppap
//元素的id
console.log(asd.id);//asd
//更改元素的className
asd.className = 'aaaa';
//获取元素特性 如果没有返回null
var className1 = asd.getAttribute('class');
console.log(className1); //aaaa
var className2 = asd.getAttribute('asdhkasdhjk');
console.log(className2); // null
//设置元素的特性 如果是不存在的特性那么就新建一个添加 否则直接更改
asd.setAttribute('lalal','asdasdasd');
//移除元素属性
asd.removeAttribute('lalal');
在获取行内样式和元素的方法时, 这两种方式会有差别,但在正常项目其实还是使用属性获取特性的方式居多,get方式用于获取自定义属性居多
// 在获取元素的样式时 通过getAttribute 返回标签内style的属性,没有为null ,而用属性方式获取返回的是对象
console.log(asd.getAttribute('style')) // background:#ffcc00
console.log(asd.style);//CSSStyleDeclaration
// 在获取元素的事件时 通过getAttribute或属性获取返回的值是不同的
// 如果元素标签内未绑定事件 getAttribute返回null 而用属性方式不管是标签绑定还是代码绑定都可以获取
console.log(asd.getAttribute('onclick')) //console.log('asd')
console.log(asd.onclick) //ƒ onclick(event) {console.log('asd')}
Element元素提供了attributes属性 是一个NamedNodeMap集合类似Nodelist,也提供了增删改查的方法,但并不常用
//获取元素
var asd = document.getElementById('asd');
/*
getNamedItem 根据特性名获取特性 格式class="ppap"
setNamedItem 设置特性, 只能是attr节点
可以通过nodeValue 获取特性值
也可以通过索引或者item()的形式获取特性,不过不同浏览器顺序可能不同
removeNamedItem 移除特性
*/
console.log(asd.attributes.getNamedItem('class')); // class="ppap"
console.log(asd.attributes.getNamedItem('class').nodeValue); // ppap
var att = document.createAttribute('asdsad');
att.nodeValue ='asdjkashdkj'
asd.attributes.setNamedItem(att);
console.log(asd.attributes.item(0));//id="asd"
console.log(asd.attributes['data-nihao'].nodeValue);//nihao
console.log(asd.attributes.removeNamedItem('data-nihao'));//data-nihao="nihao"
Element提供了创建元素的方法 我们也可以通过dom查询方法来查询element节点的后代节点
//获取元素
var asd = document.getElementById('asd');
/*
createElement,用来创建Element元素,但元素需要结合append添加到页面中
*/
var div = document.createElement('div');
div.innerText = 'ppap';
asd.appendChild(div);
/*
如果我们获取一个节点,但是还想要获取指定的后代元素可以使用 查询方法
就会获取后代节点了
*/
var spanList = asd.getElementsByTagName('span');
console.log(spanList); //HTMLCollection [span.sadas]
- Text类型
Text类型表示可以按照字面解释的纯文本内容,text没有子节点,相邻两个文本节点没有空格
<script>
//获取元素
var asd = document.getElementById('asd');
//创建文本节点 元素节点添加文本节点
var text= document.createTextNode('text');
asd.appendChild(text);
//文本节点的值 可以通过nodeValue获取
var textContent = asd.childNodes[3].nodeValue;
console.log(textContent); //text
//文本节点添加文本节点
text.appendData('append')
//console.log(text)//textappend
//删除文本,从指定位置删,第二个参数多少个字符
text.deleteData(2,3)//teppend
//从指定位置插入文本
text.insertData(2,'hehe')//teheheppend
//从指定位置替换 替换两个字符 替换文本
text.replaceData(3,2,'dada')//tehdadaeppend
//从指定位置分割文本节点, 会分割成两个文本节点 返回后一个节点
console.log(text.splitText(5).nodeValue)//daeppend
//从指定位置截取字符串 第二个参数截取个数
console.log(text.substringData(0,4));//tehd
</script>
- Comment类型
注释节点跟文本节点继承于一个基类, 所以我们可以用操作文本节点的方式去操作注释节点,创建注释节点用document.createComment()方法
- DocumentType类型
文档类型, 一般都放在document.doctype中 document.doctype.name 记录了<! doctype 后面的内容
- Attr类型
Attr类型 表示元素的特性, 特性就是存在于 attributes中的节点, 但是它并不属于Dom文档树的一部分
var asd = document.getElementById('asd');
var att = document.createAttribute('data-ppap');
att.nodeValue = 'hehe'
asd.attributes.setNamedItem(att);
2.DOM操作技术:
- 使用nodeList
所有的动态集合(nodeList,HTMLCollection,NameNodeMap)都是都是实时查询的,在我们迭代nodelist的时候如果用nodeList.length进行比较是有可能造成死循环的
var liList = document.getElementsByTagName('li')
for (let index = 0; index < liList.length; index++) {
document.body.appendChild(document.createElement('li')) //死循环
}
书中建议我们在进行是迭代时,再创建一个变量来存储list.length(类似快照),然后让循环条件跟这个变量比较, 同时我们应该尽量减少访问nodeList的次数,每运行一次都会对文档进行查询
var liList = document.getElementsByTagName('li')
for (let index = 0,llength = liList.length; index < llength; index++) {
document.body.appendChild(document.createElement('li'))
}