0705 NOTE
1.节点操作
在 HTML DOM (Document Object Model) 中 , 每一个元素都是 节点:
文档是一个文档节点。
所有的HTML元素都是元素节点。
所有 HTML 属性都是属性节点。
文本插入到 HTML 元素是文本节点。
注释是注释节点。
节点与元素:
元素(element):页面中所有的标签,即使元素也是节点
节点(node):页面中所有的内容,包括标签、属性(标签的属性)、文本(文字,换行,空格,回车))
节点的遍历
console.log(document.body);//body
console.log(document.title);//title
console.log(document.head);//head
console.log(document.documentElement);//html
console.log(document);//是整个文档
console.log(document.URL);//location.href
console.log(document.domain);//域名
console.log(document.styleSheets);//CSS的style列表
div.parentElement//父元素
div.parentNode//父节点
div.children//子元素 html标签
div.childNodes//子节点
div.firstChild//第一个子节点
div.firstElementChild//第一个子元素
div.lastChild//最后一个子节点
div.lastElementChild//最后一个子元素
div.previousSibling//向上一个兄弟节点
div.previousElementSibling//向上一个兄弟元素
div.nextSibling//向下一个兄弟节点
div.nextElementSibling//向下一个兄弟元素
节点的增删改查
var a=document.createElement("a"); //创建元素<a>
var div=document.querySelector("div"); //查询第一个div标签
var span=document.querySelector("span");//查询第一个span标签
//document.querySelector返回文档中匹配指定的css选择器的第一元素
div.appendChild(a); //向父元素div中插入子元素a
父容器.insertBefore(新标签,插入在谁的前面) //插在父元素下的某个子元素之前
div.insertBefore(a,span); //将span元素插在父元素div下子元素a之前
div.innerHTML="<div></div>"; //父元素div下写入标签div
div.innerText="<div></div>" //父元素div下写入文本"<div></div>" //仅是文本
div.textContent="<div></div>" //将父元素div中的所有元素替换为这个abc的文本节点
document.createTextNode("文本内容"); //创建文本节点
父容器.replaceChild(新子节点,旧子节点); //元素替换
div.replaceChild(a,span); //将父元素div下的子元素span标签替换为a标签
旧子节点.replaceWith(新子节点) //元素替换
span.replaceWith(a); //将span标签替换成a
div.removeChild(span); //删除父元素div下子元素第一个span标签
span.remove(); //删除span
// 删除指从界面()body中删除这个html标签 从body删除ul,但是ul仍然存在,在内存里
// 删除只是从界面中删除,但是变量仍然存储这个元素,但是如果是以前的innerHTML清除后 就无法找回了
新元素=原元素.cloneNode(false)
//浅复制 可以复制源标签的所有属性和样式,不包含内容和后代
//如果传递参数为true,还将递归复制当前节点的所有子孙节点,否则只复制当前节点
var 目标对象=Object.assign(目标对象,源对象1,源对象2...);
//浅复制,将后面的对象合并复制到前面的对象中
继承链:
HTMLDivElement–>HTMLElement–>Element–>Node–>EventTarget–>Object
HTMLDocument–>Document–>Node–>EventTarget–>Object
虚拟DOM 复制DOM(不复制属性等),解决元素属性更改页面就重绘的问题
2.DOM属性
标签属性分为:系统标签属性 自定义标签属性
系统标签属性会影响DOM生成后的对象属性,从而引起元素变化
自定义标签属性:不会自动引起DOM对象的变化,可以通过程序或者css设置产生变化
标签属性,特征是全字符 属性名和属性值都是字符,命名时遵照-方式命名
DOM的对象属性和标签属性 不一定相同,也不一定可以互相获取
DOM中的自定义属性和标签自定义属性是完全不能互相调用的
有些标签默认属性可以通过DOM属性调用:
title id src href alt placeholder type value 等等
class标签属性DOM属性调用是className
check标签:html里属性值是check,DOM里属性值是true/false
DOM对象.setAttritube("标签属性名","标签属性值");
DOM对象.removeAttritube("标签属性名");
DOM对象.getAttribute("标签属性名");//根据标签属性名获取属性值
//修改和新增标签属性
div.setAttribute("data-main","20");
div.setAttribute("xietian","1234");
//获取标签属性
console.log(div.getAttribute("data-main"));
//删除标签属性
div.removeAttribute("xietian")
div.removeAttribute("a");
// 标签属性值如果和标签属性名相同时,不用写值
// 很多系统标签属性大多数是对于标签的初始化属性设置,更改时,减少对于系统标签属性的值更改
3.DOM样式
style属性只能给dom对象设置行内样式
querySelectorAll()选择多个元素,返回值是NodeList,不能设置样式
//获取计算后样式
getComputedStyle(div).width;
console.log(div.currentStyle.width);//IE8一下兼容写法/
dom样式写入:
//对象属性样式写入
div.style.width="50px";
//设置样式时只能设置非伪类或者伪元素的DOM样式
//设置样式时,如果样式属性名多个词汇构成 background-color
//在这里设置时写为backgroundColor,去掉-并把后面的字母变成大写
//行内样式直接写入
div.style="width:50px;height:50px;background-color:red";
//构造设置样式的函数
function setStyle(elem, style) {
for(var prop in style){
elem.style[prop]=style[prop];
}
setStyle(div, {
width: "50px",
height: "50px",
backgroundColor: Utils.randomColor(1),
position:"absolute",
left:e.x-25+"px",
top:e.y-25+"px"
});
//通过object.assign()实现设置样式
Object.assign(div.style,{
width: "50px",
height: "50px",
backgroundColor: Utils.randomColor(1),
position:"absolute",
left:e.x-25+"px",
top:e.y-25+"px"
})
js中添加css达到隐藏页面中css的目的
4.DOM样式常见属性
console.log(div1.clientWidth,div1.clientHeight);//width+padding-17px(滚动条宽高)
console.log(div1.offsetWidth,div1.offsetHeight);//width+padding+border
console.log(div1.scrollWidth,div1.scrollHeight);//内容的宽高
console.log(document.documentElement.clientWidth,document.documentElement.clientHeight);//可视区域的宽高
console.log(document.body.clientWidth,document.body.clientHeight);//实际内容的高度,宽度就是body宽度-margin
console.log(document.documentElement.offsetWidth,document.documentElement.offsetHeight);//html内容的高度
console.log(document.body.offsetWidth,document.body.offsetHeight);//同client相同
console.log(document.documentElement.scrollWidth,document.documentElement.scrollHeight);//如果html内容高度小于可视区域高度,获取可视区域高度,否则获取内容高度
console.log(document.body.scrollWidth,document.body.scrollHeight);//获取body的内容宽高
console.log(div1.clientLeft,div1.clientTop);//边线粗细
console.log(div1.offsetLeft,div1.offsetTop);//如果有定位,等同于left和top的结果,如果没有定位实际到页面左上角的位置
console.log(div1.scrollLeft,div1.scrollTop);//div1自身x,y滚动条的位置
// 可以获取也可以设置
div1.onscroll=function(){
console.log(div1.scrollLeft,div1.scrollTop);
}
// 仅scrollTop和scrollLeft可以设置
div1.scrollTop=400;
// 实际页面中滚动条是html的scrollLeft和scrollTop
console.log(document.documentElement.scrollLeft,document.documentElement.scrollTop);
// document.documentElement.scrollTop=300;
宽高
属性 | 中文含义 | 解释 |
---|---|---|
clientWidth clientHeight | 客户宽高 | 边框(不含)以内的所有宽高之和-滚动条宽高17px |
offsetWidth offsetHeight | 偏移宽高 | DOM元素在页面中占位的宽高 |
scrollWidth scrollHeight | 滚动区域宽高 | 会变为内容宽度 |
位置
属性 | 中文含义 | 解释 |
---|---|---|
clientLeft clientTop | 客户位置 | 边线宽高 |
offsetLeft offsetTop | 偏移位置 | 元素左上角(占位的左上顶点)的相对位置 |
scrollLeft scrollTop | 滚动条位置 | 滚动条位置 |
offsetLeft offsetTop父元素有定位,相对于父元素;父元素无定位,相对于body
scrollLeft scrollTop 滚动条距离上端 可读可写
X/CH=ST/SH
初始化第一次渲染时,页面中的滚动条不可以设置,当第一次渲染完毕,交互时有效
5.事件基础
耦合和解耦:
耦合是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。
在软件工程中,对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护成本越高,因此对象的设计应使类和构件之间的耦合最小。
在软件工程中,降低耦合度即可以理解为解耦,模块间有依赖关系必然存在耦合,理论上的绝对零耦合是做不到的,但可以通过一些现有的方法将耦合度降至最低。
设计的核心思想:尽可能减少代码耦合,如果发现代码耦合,就要采取解耦技术。让数据模型,业务逻辑和视图显示三层之间彼此降低耦合,把关联依赖降到最低,而不至于牵一发而动全身。原则就是A功能的代码不要写在B的功能代码中,如果两者之间需要交互,可以通过接口,通过消息,甚至可以引入框架,但总之就是不要直接交叉写。
观察者模式:观察者模式存在的意义就是「解耦」,它使观察者和被观察者的逻辑不再搅在一起,而是彼此独立、互不依赖。比如网易新闻的夜间模式,当用户切换成夜间模式之后,被观察者会通知所有的观察者「设置改变了,大家快蒙上遮罩吧」。QQ消息推送来了之后,既要在通知栏上弹个推送,又要在桌面上标个小红点,也是观察者与被观察者的巧妙配合。
事件Event 事件侦听 事件抛发
系统事件:
事件句柄(Event Handlers)
自定义事件:
如果需要接收和抛发事件,必须要有对应的对象,接收和抛发的对象
接收和抛发的对象必须是同一个,而且必须是继承EventTarget类型的对象
所有的DOM对象都是可以抛发和接收事件的
接收和抛发对象必须一致,侦听事件类型必须和抛发事件类型一致,侦听必须在先,抛发在后
侦听事件的函数中,有且仅有一个参数,这个函数就是抛发的事件对象
// 创建一个事件目标对象
var o=new EventTarget();
// 给事件目标对象侦听chilema事件,当收到事件时执行函数
o.addEventListener("chilema",chileHandler);
// 创建一个事件对象,事件对象的事件名为chilema
var evt=new Event("chilema");
// 把这个事件对象抛发给刚才创建的事件目标对象
o.dispatchEvent(evt);
// 事件函数有且仅有一个参数,并且这个参数就是事件抛发事件对象
// 事件函数中不能有多个参数,收到事件后才会执行事件函数,因此,这个函数一定是回调函数
// 事件函数不能使用return返回结果,但是可以使用return跳出
function chileHandler(e){
console.log(e===evt);
}
/*
1、侦听事件
2、抛发事件
侦听事件在抛发事件之前
*/
//解耦,用document做中介
var o1={
a:1,
init:function(){
var evt=new Event("data");
evt.a=this.a;
document.dispatchEvent(evt);
}
}
var o2={
a:10,
init:function(){
document.addEventListener("data",this.dataHandler);
},
dataHandler:function(e){
console.log(e.a);
}
}
o2.init();
o1.init();
6.事件原理
目标对象.addEventListener(事件类型,事件回调函数,是否捕获阶段触发) 默认false
目标对象.addEventListener(事件类型,事件回调函数,{once:true}是否只侦听一次) 默认false
//{bubbles:true}就会让抛发的事件冒泡
事件是针对事件目标对象的,来抛发内容
但是并不是直接抛发过去,尤其是针对DOM元素,根据 树型结构 来传递事件
事件的三个阶段:捕获(外→内)→目标→冒泡(内→外)
事件类型 :
系统事件类型 被分为多个类别 例如 Event,MouseEvent,事件类型必须是字符串
事件执行函数 :
有且仅有一次参数,不能通过事件直接传入参数内容,因为事件执行函数是一个事件处理后回调的函数,所以只能传入函数名
如果传参就需要执行才可以,但是回调函数必须是函数名,因此不能在这里传参事件执行函数是一个回调执行的,在事件处理中并没有处理这个回调执行的结果,也就意味着
事件执行函数不能是return返回数据
事件执行函数里面的this发生了改变
事件是否在捕获阶段被触发,默认是false,冒泡阶段触发
事件对象中的方法e.stopPropagation()是阻止传递
侦听的事件目标对象和被点击到的目标对象并不是相同的
div1.addEventListener("click",divclickHandler1);
function divclickHandler1(e){
console.log("div1","___________")
console.log("this:",this)
console.log("e.currentTarget:",e.currentTarget)
console.log("e.srcElement:",e.srcElement)
console.log("e.target:",e.target)
}
//无论侦听的事件目标对象是谁,点击到的都是最外部的目标对象,但是e.srcElement和e.target指向的都是侦听的事件目标对象
在事件触发函数中
e.target和e.srcElement(IE兼容)相同:
事件 触发 的目标对象(比如可能是内部子元素)
this和currentTarget相同:
事件 侦听 的目标对象(设置侦听的)
事件委托
将子元素或者后代元素的事件委托给父元素
减少事件侦听的增加,防止内存泄漏
案例:
<style>
.city ul{
max-height: 0px;
transition: all 0.4s;
overflow: hidden;
}
.city ul[open]{
max-height: 1000px;
}
</style>
<body>
<ul class="city">
<li>北京
<ul>
<li>海淀</li>
<li>朝阳</li>
<li>昌平
<ul>
<li>小汤山</li>
<li>沙河</li>
<li>回龙观</li>
<li>天通苑</li>
<li>霍营</li>
<li>阳坊</li>
</ul>
</li>
<li>大兴</li>
<li>密云</li>
<li>顺义</li>
</ul>
</li>
<li>河北</li>
<li>山西</li>
<li>陕西
<ul>
<li>西安</li>
<li>咸阳
<ul>
<li>三原</li>
<li>礼泉</li>
<li>淳华</li>
<li>富平</li>
<li>泾阳</li>
</ul>
</li>
<li>宝鸡</li>
<li>铜川</li>
<li>榆林</li>
</ul>
</li>
<li>河南</li>
<li>山东</li>
</ul>
</body>
var ul=document.querySelector(".city"); //将dom中class名为city的第一个元素标签赋给ul
ul.addEventListener("click",clickHandler); //给ul添加一个点击事件侦听
function clickHandler(e){
if(e.target.nodeName!=="LI") return //如果目标不是li标签,直接返回
// if(e.target.constructor!==HTMLLIElement) return;
// console.log(e.target,e.srcElement);
// console.log(this.firstElementChild.getAttribute("open"));
if(e.target.children.length===0)return; //如果目标节点没有子元素标签,直接返回
e.target.firstElementChild.getAttribute("open") ? e.target.firstElementChild.removeAttribute("open") : e.target.firstElementChild.setAttribute("open","open");
} //当前标签的第一个元素子节点是否存在获取的属性节点"open",存在则删除,不存在则添加