0. 什么是BOM和DOM?
BOM指的是浏览器对象模型,它指的是把浏览器当作一个对象来对待,这个对象主要定义了与浏览器进行交互的方法和接口。BOM的核心是window,而window对象具有双重角色,它既是通过js访问浏览器的窗口的一个接口,又是一个全局对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象window上的一个属性或方法存在。window对象含有location对象,navigator对象,screen对象等子对象,并且DOM的最根本的document对象也是BOM的window对象的子对象
DOM指的是文档对象模型,它指的是把文档当作一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。
0-1. BOM 对象
0-2. DOM对象
- DOM常见的操作:
- 创建节点
- 查询节点
- 更新节点
- 添加节点
- 删除节点
-
虚拟dom转真实dom
// 虚拟dom const vnode = { tag: 'div', props: { id: 'app' }, children: [ { tag: 'p', props: { className: 'text' }, children: [ 'hello world!!!' ] } ] } // 渲染函数 function _render(vnode) { if (typeof vnode === 'string') { // 创建一个文本节点 ---- createTextNode return document.createTextNode(vnode) } // 创建新元素,接受一个参数,即要创建元素的标签名 ---- document.createElement const dom = document.createElement(vnode.tag) if (vnode.props) { Object.keys(vnode.props).forEach(key => { const attr = vnode.props[key] // 在指定元素中添加一个属性节点,如果元素中已有该属性改变属性值 ---- setAttribute dom.setAttribute(key, attr) }) } // 把一个子节点添加到父节点的最后一个子节点 ---- appendChild vnode.children.forEach(child => dom.appendChild(_render(child))) return dom }
<div id="app"> <p class="text">hello world!!!</p> </div> // 真实dom转化为虚拟dom const dom2tree = (node) => { const obj = {} if(node.tagName) { obj.tag = node.tagName } else { // 文本节点 return node.data } obj.props = {} // 获取元素的所有属性attributes ---- node.attributes let len = node.attributes.length for(let i=0;i<len;i++) { obj.props[node.attributes[i].nodeName] = node.attributes[i].nodeValue } obj.children = [] node.childNodes.forEach(child => { obj.children.push(dom2tree(child)) }) return obj }
1. 事件模型
事件绑定/事件监听
概念:某些组件被执行了某些操作后,触发某些代码的执行。
* 事件:某些操作。如: 单击,双击...
* 事件源:组件。如: 按钮、文本输入框...
* 监听器:回调函数。
* 常见的事件:
1. 点击事件:
1. onclick:单击事件
2. ondblclick:双击事件
2. 焦点事件
1. onblur:失去焦点
2. onfocus:元素获得焦点。
3. 加载事件:
1. onload:一张页面或一幅图像完成加载。
4. 鼠标事件:
1. onmousedown 鼠标按钮被按下。
2. onmouseup 鼠标按键被松开。
3. onmousemove 鼠标被移动。
4. onmouseover 鼠标移到某元素之上。
5. onmouseout 鼠标从某元素移开。
5. 键盘事件:
1. onkeydown 某个键盘按键被按下。
2. onkeyup 某个键盘按键被松开。
3. onkeypress 某个键盘按键被按下并松开。
6. 选择和改变
1. onchange 域的内容被改变。
2. onselect 文本被选中。
7. 表单事件:
1. onsubmit 确认按钮被点击。
2. onreset 重置按钮被点击。
1. 原始事件模型
-
绑定方式一:标签内直接绑定
//例: < button type="button" onclick="change()" id="btn">点击< /button> <script> function change(){ var b=document.getElementById('btn'); b.innerText='登录'; } </script>
-
绑定方式二:使用js动态绑定
//例: < button type="button" id="btn">点击< /button> <script> var btn=document.getElementById('btn'); btn.onclick = function(e){ btn.innerText='登录'; alert(e.target.value); } </script>
特性:
- 绑定速度快:但由于绑定速度太快,可能页面还未完全加载出来,以至于事件可能无法正常运行;
- 只支持冒泡,不支持捕获;
- 同一个类型的事件只能绑定一次
2. 标准事件模型
绑定方式:使用addEventListener() 添加事件监听
//例:
< button type="button" id="btn">点击< /button>
<script>
var b=document.getElementById('btn');
b.addEventListener('click',function (change) {
b.innerText='登录';
},true);
</script>
标准事件模型DOM事件流的三个阶段
标准事件模型DOM事件流的三个阶段包括事件捕获阶段,处于目标阶段,事件冒泡阶段;
(1)事件捕获阶段:当我们在 DOM 树的某个节点发生了一些操作,例如单击,就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到触发的目标节点,在到达目标节点之前的过程,就是捕获阶段。捕获阶段的任务就是建立这个事件的传递路线,以便后面冒泡阶段顺着这条路线返回 Window。
(2)处于目标阶段:当事件不断的传递直到目标节点的时候,最终在目标节点上触发这个事件,就是处于目标阶段。
(3)事件冒泡阶段:事件冒泡即事件开始时,由事件发生所在的节点开始,然后逐级向上传播。
window.addEventListener监听的是哪个阶段的事件?
// 取决于window.addEventListener的第三个参数,第三个参数默认为false
//例:
//冒泡阶段
window.addEventListener('click',()=>{
},false)
//捕获阶段
window.addEventListener('click',()=>{
},true)
2. 事件冒泡
事件的冒泡:-所谓的冒泡指的是事件的向上传导,当后代元素上的事件(click、keydown…)被触发时,其祖先元素的相同事件会被自动触发
事件冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签。
-
目前不支持冒泡的事件:
- UI事件:
load
unload
scroll
resize - 焦点事件:
blur
focus - 鼠标事件
mouseleave
mouseenter
原因是在于:这些事件仅发生于自身上,而它的任何父节点上的事件都不会产生,所有不会冒泡。
- UI事件:
-
取消事件冒泡
function stopPropagation(event) { //阻止冒泡 event = event || window.event; //W3C标准 Propagation/,prɒpə'ɡeɪʃən/ if(event.stopPropagation){ event.stopPropagation(); }else{//IE标准 event.cancelBubble = true; } }
-
阻止浏览器的默认行为
什么是默认事件?
- a 标签 href 属性上的跳转。
- 鼠标右键呼出菜单。
- form 表单里 button 标签和 type属性为 submit 的 input 标签的提交。
function cancelHandler(event ){ var event = event ||window.event; //W3C标准 if(event.preventDefault){ event.preventDefault(); }else{//IE标准 event.returnValue = false; } }
3. 事件代理/事件委托
事件代理就是把一个元素的响应事件函数委托到另一个元素上,就是说本来应该加在子元素身上的事件,我们把事件加在了其父级元素身上。事件代理的原理是DOM元素的事件冒泡。
好处:
- 代码简洁,效率高,比如,不用for循环为子元素添加事件
- js新生成的子元素也不用新为其添加事件了,程序逻辑上比较方便
- 减少浏览器内存占用
5. 自定义一个通用的事件监听函数【包括事件代理】
/**
* elem表示要进行监听的元素
* type表示事件类型
* fn表示事件触发时的操作
* selecter表示事件代理的元素
*/
function bindEvent(el, type, fn, selector) {
// 普通的事件监听
if(!selector) {
el.addEventListener(type, fn);
return
}
// 事件委托
el.addEventListener(type, event => {
let element = event.target
//事件触发的真实元素是否匹配期望的选择器
if(element.matches(selector)) {
fn.call(element, event)
}
})
}
var parent = document.querySelector('.parent')
var son1 = document.querySelector('.son1')
// 普通点击事件
bindEvent(son1, 'click', e => {
e.stopPropagation()
console.log(e.target)//e.target是事件触发的真实元素
console.log(e.currentTarget)//e.currentTarget是事件绑定的元素
})
// 事件委托
bindEvent(parent, 'click', e => {
console.log(e.target)
console.log(e.currentTarget)
}, '.son3')