标签
var x = document.querySelectorAll("a[target]");
//查找文档中所有的 , 和 元素
var x = document.querySelectorAll("h2, div, span");
代码测试:
// demo
// HTMl
// Script
const infoDom = document.querySelectorAll('.info');
element.getElementsByTagName //
// demo
// HTMl
// Script
const divDom = document.getElementsByTagName('div');
const pDom = divDom[0].getElementsByTagName('p');
作者:跳跳的小记
链接:https://juejin.im/post/5ad4474e6fb9a028ba1ff230
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
■ 那到底要用哪个API来获取元素的?
工作中用querySelector 和querySelectAll ,而且querySelector()内可以使用CSS语法结构
document.querySelector('div>span:nth-child(2)')
document.querySelectorAll('div>span:nth-child(2)')[1]//获取第一个div元素中span元素中第二个span元素
做demo直接用idxxx,千万别人发现哦
不推荐:要兼容IE的可怜虫才用getElement(s)Byxxx
不过有时候还是有用的:假设
id被篡改 或是跟
全局属性冲突 时,例如parent
全局属性冲突
■ querySelectorAll与querySelector 的区别
✔ querySelector 仅返回符合 Selector 条件的第一个节点内容,是个 Node。
//HTML
left
right
//JS
var div = document.querySelector("div");
console.log(div);
显示如下:
如果获取不存在的节点呢:
var div = document.querySelector("p");
console.log(div);
显示如下:
✔ querySelectorAll 返回符合 Selector 条件的所有节点内容,是个 NodeList;
返回指定元素节点的子树中匹配selector的节点集合,采用的是深度优先预查找;如果没有匹配的,这个方法返回空集合
//HTML
left
right
//JS
var div = document.querySelectorAll("div");
console.log(div);
显示如下:
如果查找不存在的节点:
var div = document.querySelectorAll("p");
console.log(div);
querySelectorAll可以选择多个节点,以","分隔开,返回的是个数组
✔ 注意:获取元素后要加上下标[]
因为获取的元素时(伪)数组,可能有多个,所以必须用下标来指定某一元素
例:document.getElementsByTagName('div')[1].style.border='1px solid red'
■ 获取特定元素
获取html元素
获取head元素
获取body元素
获取窗口(注意窗口不是元素)
window
window.onclick=()=>{
console.log('hi')
}
获取所有元素
注意:这个document.all是个奇葩,是第六个false值,常常用来区别是否伪IE浏览器
区别是否额为IE浏览器
if(document.all){
console.log('ie浏览器');
}else{
console.log('其他浏览器');
}
Chrome浏览器下测试
■ 元素的6层原型链
抓一只div来看看!
let div=document.getElementByTagName('div')[2]
console.dir(div)
元素的6层原型链
记住:每个层构造函数都会往div(元素)身上加东西
■ 节点?元素?傻傻分不清楚
元素是节点的其中一种
MDN有完整描述,X.nodeType 得到一个数字
1.表示元素Element也叫做标签Tag
3.表示文本Text
8.表示注释Comment
9.表示文档Document
11.表示文档片段DocumentFragment
记住1和3即可
1.表示元素Element也叫做标签Tag
3.表示文本Text
节点的增删改查
程序员的宿命就是
增删改查
■ 增
语法: document.createElement()
let div1=document.createElement('div')
document.createElement('style')
document.createElement('script')
document.createElement('li')
语法: document.createTextNode()
text1=document.createTextNode('你好')
三种方法( appendChild、innerText、textContent)
div1.appendChild(text1)
div1.innerText='你好'
div1.textContent='你好'
注意:appendChild不能直接写入文本,只能写入文本节点
错误:div1.appendChild('你好')
注意:你创建的标签默认处于JS线程中,你必须把它插入到head或者body里面,它才会生效
✔ 插入页面中
appendChild
document.body.appendChild(div)或者已在页面中元素.appendChild(div)
✔ 用于新增儿子(标签里面插入标签)
appendChild
问题:页面中有div#test1和div#test2
let div=document.createElement('div')
test1.appendChild(div)
test2.appendChild(div)
请问最终div出现在哪里?
答案:test2里面。 一个元素不能出现两个地方,除非复制 一份。
✔ 克隆节点(实现上述复制)
cloneNode(deep) deep有两个参数:true/false
true: 深度克隆,后代节点也克隆
false:浅克隆
div1.style.backgroundColor='white'
div1.style.fontSize='100px'
let div2=div1.cloneNode(true)
head下的dvi1
body下的dvi2
Node原生内置中没有insertAfter()。不过,可以使用insertBefore和Node.nextSibling来模拟它
parentNode.insertBefore(node2, node.nextSibling);
//node2:插入节点
//node.nextSibling:当前节点的下一节点
如果node没有下一个节点,则它肯定是最后一个节点,则node.nextSibling返回
null
,且node2被插入到子节点列表的最后面(即node后面)
返回值:函数返回被插入过的子节点
Node.insertBefore()
在参考节点之前插入一个拥有指定父节点的子节点。
parentNode.insertBefore(node2, node);
//node2:插入节点
//node:当前节点
通过insertBefore方法可以将node2插入到node前面,如果node是
null
则将node2插入到Node的尾部。
如果node2是一个已经存在在文档中的
DOM
,insertBefore则会表现为移动该
DOM
(将会保留所有的事件)。
返回值:函数返回被插入过的子节点
✔ 示例:
Node原生内置中没有wrap()新增爸爸。不过,可以使用Node.insertBefore()和appendChild()来模拟它
node.classList.add(className)
node.classList.remove(className)
node.addEventListener(eventName, fn)
//HTML
点这里!
//JavaScript
const buttonElement = document.getElementById('btn');
buttonElement.addEventListener('click', function (event) {
alert('Element clicked through function!');
});
// 由于兼容性原因,一个带有 handleEvent 函数属性的对象也可以达到相同的效果。
buttonElement.addEventListener('click', {
handleEvent: function (event) {
alert('Element clicked through handleEvent property!');
}
});
node.removeEventListener(eventName, fn)
删除使用removeEventListener()方法添加的事件。
element.addEventListener("mousedown", handleMouseDown, true);
element.removeEventListener("mousedown", handleMouseDown, true); // 成功
■ 删
✔ 旧方法(淘汰):parentNode.removeChild(childNode)
原理:找到你爸爸删除你儿子,智障!(不用)
let div1=document.createElement("div")
let text1=document.createTextNode("你好")
div1.appendChild(text1)
document.body.appendChild(div1)
div1.parentNode.removeChild(div1)
旧方法删除
✔ 新方法:childNode.remove()
IE不支持,是后发明的
let div1=document.createElement("div")
let text1=document.createTextNode("你好")
div1.appendChild(text1)
document.body.appendChild(div1)
div1.remove()
div1.remove()新方法删除
问题:如果一个node被移除页面(DOM树),还能恢复吗?
答案:可以的,两种方法删除后,还可以进行恢复!重新添加即可(移除只是被放回到内存中)
恢复node
使node的值等于为null
div1.remove()
div1=null//与内存断开联系,被垃圾回收掉
完全抹掉
没有专门的函数,可以遍历removeChild来实现
var element = document.getElementById("top");
while (element.firstChild) {
element.removeChild(element.firstChild);
}
■ 改
✔ 改class:
div.className='red blue'
再加:div.className +='green'
class是保留字,所有只能用className代替
className
✔ 改class:
查询元素的class:div.classList
div.classList.add('green')
✔ 改Id:
div.id='xxx'
✔ 改style:(淘汰,会覆盖其他属性)
div.style='width:100px;color:blue;'
✔ 针对style某部分进行修改:
div.style.width='200px'
✔ 大小写:(淘汰)
div.style['background-color']='black'
✔ 大小写:
div.style.backgroundcolor='white'
语法:div.setAttrbute();现不常用,库开发者会用到
div.setAttrbute('data-x','text')
key:data-x
value:text
✔ 获取data-*值
如果属性是以"data-"开头,那么可通过dataset. 进行查找
方法一:div.getAttribute('data-x')
方法二:div.dataset.x
✔ 改data-*值
div.dataset.x='frank'
Element.setAttribute()
设置指定元素上的某个属性值。如果属性已经存在,则更新该值;否则,使用指定的名称和值添加一个新的属性。
如果指定的属性已经存在,则其值变为传递的值。如果不存在,则创建指定的属性。
//HTML
Hello World
//JS
var b = document.querySelector("button");
b.setAttribute("name", "helloButton");
b.setAttribute("disabled", "");
方法一:div.classList/a.href
方法二:div.getAttribute('class')/a.getAttribute('href')
两种方法都可以,但值可能会稍微有些不同
✔ 方法一:
方法一:div.classList
方法一:a.href
✔ 方法二:
方法一:div.getAttribute('class')
方法二:a.getAttribute('href')
✔ getAttribute
getAttribute()
返回元素上一个指定的属性值。如果指定的属性不存在,则返回
null
或
""
getAttribute()
■ 改事件处理函数(on开头属性)
默认点击div不会有任何事情发生
但是如果你把div.onclick改为一个函数fn,那么点击div的时候,浏览器就会调用这个函数
点击test调用函数
并且是这样调用的fn.call(div,event)
div会被当做this,event则包含了点击事件的所有信息,如坐标
利用fn.call(div,event)调用函数
是div.onclick的升级版,之后会单独讲
innerText与textContent,两者几乎没有区别
✔ div.innerText='xxx'
✔ div.textContent='xxx'
引号
间隔号
div.innerHTML='' //先清空
div.appendChild(div2)//再添加内容
打印出div2
div1清空后存放div2
想要一个新爸爸?
语法:appendChild()//直接这样就可以,直接从原来的地方消失
newParent.appendChild(div)
打印出div2
把div1的爸爸改为div2
■ 查
元素是节点的一部分
方法一:node.parentNode
方法二:node.parentElement
node.parentNode.parentNode
由Node提供:node.childNodes
由Element提供:node.children
✔ 测试一:node.childNodes 特点:可能会获取到你不需要的,HTML空格 也会被当作一个Node节点
element.childNodes
不止会获取到
DOM
,也会获取到文字等,只有当
nodeType === 1
时才表示
DOM
node.childNodes
✔ 测试二:node.children 特点:不会获取空格,只会获取标签
node.children
思考:当子代变化时,获取的也会实时变化吗?
答案:当子代变化时,
两者也会实时变化 ,但有一个例外querySelectorAll获取所有querySelectAll不会实时根据页面长度而改变自己的长度,获取一次后length就不会改变
querySelector实时变化
querySelectorAll无变化
注意:一定要排除自己!
第一步:
由Node提供:node.parentNode.childNodes
由Element提供:node.parentNode.children
parentNode.childNodes与parentNode.children
第二步:
最重要步骤:排除自己
let siblings=[] //Sibling兄弟姐妹
let a=lg.parentElement.children
for(let i=0;i
查兄弟姐妹(排除自己)
node.firstChild
document.body.children[0]
node.firstChild
document.body.children[0]
node.lastChild
node.lastChild
node.previousSibling
缺点:Gecko内核的浏览器会在源代码中标签内部有空白符的地方插入一个文本结点到文档中.因此,使用诸如 Node.firstChild 和 Node.previousSibling 之类的方法可能会引用到一个空白符文本节点, 而不是使用者所预期得到的节点.
node.nextSibling
node.nextSibling查看弟弟
缺点:Gecko内核的浏览器会在源代码中标签内部有空白符的地方插入一个文本结点到文档中.因此,使用诸如 Node.firstChild 和 Node.previousSibling 之类的方法可能会引用到一个空白符文本节点, 而不是使用者所预期得到的节点.
Element提供:查看上一个元素哥哥/姐姐(排除文本节点)
node.previousElementSibling
node.previousElementSibling查看哥哥
Element提供:查看下一个元素弟弟/妹妹(排除文本节点)
node.nextElementSibling
node.nextElementSibling查看弟弟
查
原理:DOM本身就是树数据结构
查看(遍历)一个div里面的所有元素
travel=(node,fn)=>{
fn(node)
if(node.children){
for(let i=0;i{console.log(node)})
■ 查
■ DOM操作时跨线程的
为什么DOM操作会比较慢?思考
■ 《JS世界》里讲浏览器功能划分:
■ 浏览器分为渲染引擎和JS引擎
各线程各司其职
✔ JS引擎不操作页面,只能操作JS
✔ 渲染引擎不能操作JS,只能操作页面
✔ document.body.appendChild(div1)
✔ 这句话JS是如何改变页面的?
✔ 当浏览器发现JS在body里面加了个div1对象
✔ 浏览器就会通知渲染引擎在页面里也新增一个div元素
✔ 新增的div元素所有属性都照抄div1对象
■ 插入新标签的完整过程
✔ 你对div1所有的操作都属于JS线程内的操作
✔ 浏览器会发现JS的意图
✔ 就会通知渲染线程在页面中渲染div1对应的元素
✔ 你对div1的操作都有可能 会触发重新渲染
✔ div1.id='newid'有可能会重新渲染,也有可能不会(如果包含CSS样式,则会触发)
✔ div1.title='new'可能会重新渲染,也可能不会
✔ 如果你连续对div1多次操作,浏览器可能会合并一次操作,也可能不会
■ 属性同步
✔ 对div1的标准属性的修改,会被浏览器同步到页面中
✔ 比如id、className、title等
✔ 同上
✔ 对非标准属性的修改,则只会停留在JS线程中
✔ 不会同步到页面里
比如x属性,代码如下:
JS Binjs.jirengu.com
✔ 如果你有自定义属性,又想被同步到页面中,请使用data-作为前缀
//HTML
//JS
let div1=document.querySelector('#test')
div1.id='frank' //成功 同步过去
div1.x='frank' //失败 没有同步
div1.dataset.x='frank' //成功 同步过去
■ Property v.s. Attribute
Property 属性(JS线程)
JS线程中div1的所有属性,叫做div1的property
百度翻译fanyi.baidu.com
Attribute属性(渲染线程)
渲染引擎中div1对应标签的属性,叫做attribute
百度翻译fanyi.baidu.com
✔ 大部分时候,同名的proprety和attribute值相等
✔ 但如果不是标准属性,那么它俩只会在一开始时相等,之后修改时不会同步的!
✔ 但注意attribute只支持字符串
✔ 而proprety支持字符串,布尔等类型