JavaScript的动态集合
之前讲了一点NodeList对象,HTMLCollection对象,一般情况下querySelectorAll( )方法返回的就是NodeList对象,此方法非常强大,几乎可以取到HTML中的各种元素,相对的还有childNodes 其他一些返回方法返回的都是NodeList对象
那么childNodes是指元素下的所有子节点集合,节点知识待会补充
判断NodeList
NodeList实例对象是一个类数组对象(ArrayLike),它的成员是节点对象
类数组简单理解就是一个类似数组的玩意拥有数组的特性
对于一个普通的对象来说,如果它的所有property名均为正整数,同时也有相应的length属性,那么虽然该对象并不是由Array构造函数所创建的,它依然呈现出数组的行为,在这种情况下,这些对象被称为“类数组对象”。
ArrayLike对象的精妙在于它和javascript原生的Array类似,但是它是自由构建的,它来自开发者对javascript对象的扩展,也就是说:对于它的原型(prototype)我们可以自由定义,而不会污染到javascript原生的Array。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</body>
<script>
var oI=document.querySelectorAll('li')
console.log(Array.isArray(oI)) //false
console.log(oI instanceof NodeList) //true
</script>
</html>
快速转换真数组
Array.prototype.slice.call(arrayLike)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</body>
<script>
var oI=document.querySelectorAll('li')
console.log(Array.isArray(oI)) //false
var newarr= Array.prototype.slice.call(oI)
console.log(Array.isArray(newarr)) //true
console.log(oI instanceof NodeList) //true
</script>
</html>
也就是说此时我们可以通过newarr使用数组方法做一些不可描述的事情,这部操作我简单解释一下
Array.prototype.slice表示数组的原型中的slice方法。注意这个slice方法返回的是一个Array类型的对象(返回值这个不懂的可以看我的js数组),当然splice也可以,重要的是它返回的是数组对象,调用call的是只能是方法,call的作用就是改变方法的作用域环境,而slice是数组方法也就是数组才能调用,也就是说将数组方法放置在了类数组的作用域之中,看起来就好像arrayLike也具有了数组的方法。那么IE8是不支持的,因为IE8不完全支持ESMAScript5
// var newarr= Array.prototype.slice.call(oI)
console.log(Array.isArray(Array.from(oI))) //true
ES6的Array.from
方法也可以转化为真数组,不过个人认为还是前面一个容易理解,一般情况开发下没有这种一定要转化真数组的情况
NodeList的动态集合指的是,DOM发生改变而我们获取的集合会相应的发生改变,这里了解一下,querySelectorAll( )获取的是静态的,这种扩展的概念希望小伙伴可以多了解一下,此处不演示了,有兴趣的小伙伴可以动态加载节点的时候测试一下
为什么会有NodeList对象,DOM定义了一个Node接口,这个接口在javascript中是作为Node类型实现的,NodeList对象主要就是Node节点的集合,而后面要讲的HTMLCollection只是Node节点里面的一种,叫Element元素节点,几乎所有元素下的节点集合都是HTMLCollection节点,注意是元素下,如果是document下的一般都返回NodeList对象
节点
DOM是javascript操作网页的接口,全称为文档对象模型(Document Object Model)。它的作用是将网页转为一个javascript对象,从而可以使用javascript对网页进行各种操作(比如增删内容)。浏览器会根据DOM模型,将HTML文档解析成一系列的节点,再由这些节点组成一个树状结构。DOM的最小组成单位叫做节点(node),文档的树形结构(DOM树)由12种类型的节点组成。
一般地,节点至少拥有nodeType、nodeName和nodeValue这三个基本属性。节点类型不同,这三个属性的值也不相同,
主要的常见的节点有三个:元素节点、属性节点和文本节点
nodeType:元素(1) 属性(2) 文本(3)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div id="text" index="1">测试</div>
</body>
<script>
var oU=document.querySelectorAll('ul')[0]
console.log(oU.nodeType) //1 元素节点
var test=document.querySelectorAll('div')[0].firstChild //第一个子节点 也就是文字
console.log(test.nodeType)//3 文本节点
var attr=text.attributes.id
console.log(attr.nodeType) // 2 属性节点
var attrs=text.attributes.index
console.log(attrs.nodeType) // 2 自定义属性
</script>
</html>
因为id是唯一标识,其实直接打印ID名是可以直接输出元素的(var text=document.getElementById('text')
省略了这步),name不可以,元素的属性是存在于attributes中的
var oU=document.querySelectorAll('ul')[0]
console.log(oU.nodeType,oU.nodeName,oU.nodeValue) //1 "UL" null元素节点
var test=document.querySelectorAll('div')[0].firstChild
console.log(test.nodeType)//3 文本节点
var text=document.getElementById('text')
var attr=text.attributes.id
console.log(attr.nodeType) // 2 属性节点
var attrs=text.attributes.index
console.log(attrs.nodeType,attrs.nodeName,attrs.nodeValue) // 2 "index" "1"自定义属性
nodeName与nodeValue好理解,如果没值则返回null
自定义属性
自定义属性在实际开发中还是比较常用的
getAttribute() 获取特定元素节点属性的值,某些低版本浏览器不支持.
setAttribute() 设置特定元素节点属性的值,某些低版本浏览器不支持
removeAttribute() 移除特定元素节点属性,某些低版本浏览器不支持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="text" index="1">测试</div>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</body>
<script>
console.log(text.getAttribute('index'),text.index)
text.setAttribute('test','666')
console.log(text.getAttribute('test'),text.test)
text.removeAttribute('test')
console.log(text.getAttribute('test'))
</script>
</html>
首先这里有两个坑,因为我这里使用的是谷歌所以,好像IE是可以直接使用调用属性的方式取到值,表示没试过( ̄□ ̄||)
text.indexs=2
console.log(text.indexs)//2
当然这样的自定义属性是可以取到的,而且无法查看标签中的属性,那也只是简单介绍一下,JQ的attr还是很好用的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</body>
<script>
var oI=document.querySelectorAll('li')
for(var i=0;i<oI.length;i++){
oI[i].index=i
oI[i].onclick=function(){
alert(this.index)
}
}
</script>
</html>
使用此方法还是很好使用,做一些相应的效果
节点操作
其实很多人还是喜欢字符串操作,在元素后面拼接字符串达到生成新元素的效果,如果我们直接添加节点也是可以的
之前提过childNodes子节点,childNodes子节点还包括空白符,一般情况下我们使用children
childNodes----children过滤空白节点
childNodes 获取当前元素节点的所有子节点,这里面包含空白节点,在IE9之前,IE浏览器会自动忽略空白节点(兼容问题)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="div"><p><span></span></p><h2></h2></div>
</body>
<script>
console.log(div.childNodes)// [p, h2]
</script>
</html>
span是p标签里面的,也就是p的子标签不是div的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="div"><p><span></span></p> <h2></h2></div>
</body>
<script>
console.log(div.childNodes) //[p, text, h2]
console.log(div.children) //[p, h2]
</script>
</html>
空白也会被当成节点,children会过滤空白节点
高级选取firstChild/lastChild/parentNode/previousSibling/nextSibling
firstChild/firstElementChild 获取当前元素节点的第一个子节点(后者不包括空白节点)
lastChild/lastElementChild 获取当前元素节点的最后一个子节点
previousSibling/previousElementSibling 获取当前节点的前一个兄弟节点
nextSibling/nextElementSibling 获取当前节点的后一个兄弟节点
ownerDocument 获取该节点的文档根节点(document)
document.documentElement 获取的html标签元素
parentNode 获取当前节点的父节点
可以每个都测试一下,我这里就不测了
往DOM追加DOM的操作称之为文档碎片,文档碎片在理论上可以提高DOM操作的执行效率,将要追加的dom操作先放置在文档碎片中,最后在追加给对应的元素。写个小demo演示一下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
*{
margin: 0;padding: 0;
}
li{
list-style: none;
}
</style>
</head>
<body>
<textarea name="" id="text" cols="30" rows="10">
</textarea>
<button onclick="get()">发布新闻</button>
<ul id="news">
</ul>
</body>
<script>
function get(){
var val=text.value;
if(val==''){
alert('没有新标题')
}else{
var li=document.createElement('li')
li.innerHTML=val
li.style.color='#'+Math.floor(Math.random()*0xffffff).toString(16);
news.appendChild(li)
}
}
</script>
</html>
这里有个小问题,我们追加的元素是在末尾的,不符合现实,最新添加的应该显示最前面,稍微改动一下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
*{
margin: 0;padding: 0;
}
li{
list-style: none;
}
</style>
</head>
<body>
<textarea name="" id="text" cols="30" rows="10">
</textarea>
<button onclick="get()">发布新闻</button>
<ul id="news">
</ul>
</body>
<script>
function get(){
var val=text.value;
if(val==''){
alert('没有新标题')
}else{
var li=document.createElement('li')
li.innerHTML=val
li.style.color='#'+Math.floor(Math.random()*0xffffff).toString(16);//随机颜色
if(news.children[0]){ //如果第一个子元素存在
news.insertBefore(li,news.children[0]) //插入第一个元素之前
}else{
news.appendChild(li) //不存在直接添加元素
}
}
}
</script>
</html>
在模板字符串之后,我个人用的比较少,有兴趣可以深入了解一下
合法自定义属性
之前我们说过元素的自定义属性添加与获取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
<body>
<div class="div" index="1" id="div">
</div>
</body>
<script>
div.setAttribute('indexs','2')
console.log(div.getAttribute('indexs'),div.index,div.id) //2 undefined div
</script>
</html>
针对元素的属性,如果是合法的我们可以直接调用,自定义的属性如果不使用getAttribute()获取无法取得值,返回undefined,如果我们想添加属性又是合法的怎么办
HTML5在Element对象上定义了一个dataset属性。也就是我们可以调用这个属性,属性指代一个对象,它的各各属性对应于去掉前缀data-属性。看例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
<body>
<div class="div" id="div" data-x="x" data-index="index">
</div>
</body>
<script>
console.log(div.dataset,div.dataset.x,div.dataset.index) // {x: "x", index: "index"} "x" "index"
</script>
</html>
如果是驼峰命名 data-jq-ery,就是对象中的jqEry属性,简单了解一下
关于属性就写到这里,继续深入还有其他内容,个人认为理解上面的即可,有兴趣的可以了解HTML属性(注意我前面讲的是ELement元素的属性)、数据集属性以及Attr节点的属性
元素的内容
顾名思义,元素的内容就是指元素标签内的代码,但我们知道块状元素是可以嵌套行内元素,所以里面可能是HTML字符串或者 纯文本的字符串
一般情况下,使用Element下的innerHTML 属性返回该元素的内容,建议此处少使用“+=”,重新赋值覆盖即可,因为web浏览器解析 HTML是擅长的,但是又要序列化又要解析就有点低下了
与之相反的有个属性是outerHTML,相反取到的就是除了该元素以外的内容
纯文本元素内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
<body>
<p id="p">This is a Text<i>666</i> <script>666</script></p>
</body>
<script>
console.log(p.textContent) //This is a Text666 666
console.log(p.innerText) //This is a Text666
</script>
</html>
textContent与innerText属性非常相似,但innerText不解析script标签,并且innerText不能修改一些标签的值,textContent可以,如果是文本节点的value一般都是可以修改
JQ里面的html()与text()对应相似的应用,可以了解一下,innerHTML还能解析输出html标签,有兴趣的可以查下insertAdjacentHTML()这个方法还是比较强大的,只是单词太多没啥人用,再加上JQ的便捷,最恶心的是IE的方法哈哈
网友评论