在这个行业摸爬滚打已有相当长的一段时刻了。回想当初,我以一个全然无知的身份踏入了这个行业,与现在比较,似乎并未获得显著的成长,也未能在这一行中安定自己的地位。我唯一的引路人是谷歌,每逢遇到疑问,我便向它求解。那些年,我的查找记载充满着htmlcssjs等基础知识,每一次在浏览器中探寻答案,都带给我无尽的快乐。那段韶光,虽然我对一切都所知甚少,却过得极端愉快,或许是由于我其时的心态更为单纯,高枕无忧。时至今日,那段韶光仍是我职业生涯中最快乐、最难忘的一页。但是,现在的我却鲜少能感遭到那样的快乐了!

JavaScript实际上有许多值得深化了解的方面,逐步发掘其间的奥妙对错常风趣的! 让我们一同去发掘JavaScript的闪光点—js事情

概念问题

首先,先来了解几个概念:

  • 什么是js事情?

    • 概念:JavaScript事情是指在Web页面中产生的交互性动作或状况改变,如点击按钮、键盘输入、鼠标移动等(简单了解便是体系中能够产生的各种事情(浏览器行为,用户行为)
    • 常见的事情:
      • 鼠标事情:click,dblclick,mouseover,mouseout,mousedown,mouseup,mousemove,contextmenu等,
      • 键盘事情:keydown,keyup,keypress等,
      • 进度事情:abort,error,loadend,load,loadstart,timeout,progress等,
      • 窗口事情:resize,scroll等,
      • 触摸事情(移动端):touchstart,touchend,touchend等,
  • 事情源(Event Source):事情的来源,即导致事情产生的方针或组件。

  • 事情方针(Event):在处理事情时主动创立的方针,它包含了与事情相关的信息和办法(事情方针是在事情产生时由浏览器创立的方针,包含有关事情的详细信息,如事情类型、方针元素、鼠标位置等)。

  • 事情方针元素(Event Target):是一个接口,表明能够接收事情的方针方针(在DOM中,几乎一切的节点类型都完成了EventTarget接口,包含元素节点、文档节点和文档片段等)(事情开端产生的元素(最小节点))。

  • 事情处理程序(Event Handler):确定控件如何呼应事情的事情进程(当方针(如按钮、文本框等)产生某个事情(如点击、输入等)时,会履行与此事情相应的事情处理程序)。

  • 举例(button点击)介绍这几个概念:

   <div id="demo">
       <!-- button 能够了解为事情源 -->
      <button onclick="handleClick(event)" id="button">点我</button>
   </div>
   const buttonDom = document.getElementById("button");
   // 了解为事情源
   const handleClick = (event) => { // event 便是事情方针 其间包含了许多信息
       console.log('event', event);
       console.log('event.target', event.target); // event.target 事情方针元素
   }

前端面试 第二篇  js 事情

事情流(Event Flow)

概念了解和介绍为主,事情模型中会相结合协助了解~

  • 概念:是当在Web页面中产生某个事情时,这个事情是如何在DOM树中传达和处理的。它首要包含三个阶段:捕获阶段(Capturing Phase),方针阶段(Target Phase)冒泡阶段(Bubbling Phase)。在捕获阶段,事情从最外层的先人元素开端,向下传递到方针元素;而在冒泡阶段,事情则从方针元素开端,向上冒泡到最外层的先人元素。这种传达机制使得在事情流的不同阶段都能够对事情进行处理(描绘了事情从产生到被处理的进程)。

捕获阶段(事情捕获)(Capturing Phase)

  • 了解:也称为处于捕获阶段。 在捕获阶段,事情从最外层的元素逐级向内部元素传达,直到到达事情的方针元素。在这个阶段,事情处理程序能够在父元素上捕获事情,但没有到达方针元素.
  • 作用: 能够在事情到达方针元素之前对事情进行处理或阻拦(实际运用场景较少);
  • 图解
    前端面试 第二篇  js 事情

方针阶段(Target Phase)

  • 了解:也称为处于方针阶段。在这个阶段,事情现已到达了事情的方针元素,即触发事情的DOM元素。事情处理程序会在方针元素上履行,处理与事情相关的操作。
  • 作用:事情处理程序能够拜访事情方针(Event Object),并对事情进行处理。
  • 图解

前端面试 第二篇  js 事情

冒泡阶段(事情冒泡)(Bubbling Phase)

  • 了解:也称为冒泡阶段或处于冒泡阶段。在这个阶段,事情从方针元素开端向上冒泡至DOM树的根节点。换句话说,事情首先在方针元素上触发,然后逐级向上冒泡至父级元素,直至根节点。
  • 作用:冒泡机制能够完成事情的传达和托付处理,进步页面功能和交互作用。
  • 图解

前端面试 第二篇  js 事情

事情流图解:

前端面试 第二篇  js 事情

事情模型 (Event Model)

  • JavaScript事情模型是描绘网页中事情处理的机制和流程的模型(js交互模型)。

DOM0 事情模型

概念:也被称为原始事情模型,是前期的DOM事情处理办法(DOM0事情模型是没有事情流概念的)。

特色

  • 一个DOM节点只能绑定一个事情处理器,假如再次为同一个事情绑定处理函数,那么之前绑定的事情处理函数会被掩盖;
  • 只支撑冒泡阶段;

特色代码举例

  • 只能绑定一个事情处理器:
   <div id="demo">
      <button id="button">点我</button>
   </div>
   const buttonDom = document.getElementById("button");
   buttonDom.onclick = (event) => {
       console.log('first===>', event);
   }
   // 只会履行这个
   buttonDom.onclick = (event) => {
       console.log('last===>', event); // last===> PointerEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0,…}
   }
  • 只支撑冒泡阶段:
 #grandfather {
        display: flex;
        align-items: center;
        justify-content: center;
        background: red;
        height: 200px;
        width: 200px;
    }
    #father {
        display: flex;
        align-items: center;
        justify-content: center;
        background: greenyellow;
        height: 100px;
        width: 100px;
    }
    #target {
        background: white;
        height: 50px;
        width: 50px;
        border: none;
    }
<div id="demo">
    <div id="grandfather">
        <div id="father">
            <button id="target">点我</button>
        </div>
    </div>
</div>
const grandfatherDom = document.getElementById("grandfather");
const fatherDom = document.getElementById("father");
const targetDom = document.getElementById("target");
grandfatherDom.onclick = () => {
    console.log('name', 'grandfather'); // 最终一个被履行
}
fatherDom.onclick = (event) => {
    console.log('name', 'father');  // 第二个履行
}
targetDom.onclick = (event) => {
    console.log('name', 'target'); // 第一个履行
}

前端面试 第二篇  js 事情

捕获阶段是从顶端(最外层的元素逐级向内部元素传达),是grandfather=>father=>target次序传达,上图打印输出的是次序target=>father=>grandfather和冒泡阶段(从方针元素到最外层元素)一致

两个途径

  • 行内事情(内联模型) :直接在HTML元素的特点中指定事情处理程序。例如在按钮元素的onclick特点中指定JavaScript代码来处理点击事情。
  • 单次绑定:
       <div id="demo">
          <button onclick="handleClick(event)" id="button">点我</button> 
       </div>
    
       const handleClick = (event) => {
           onsole.log('event===> ', event); // event===> PointerEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0,…}
       }
    
  • 屡次绑定:
    <div id="demo">
       <button onclick="handleClick(event, 'first')" onclick="handleClick(event, 'last')"id="button">点我</button> 
    </div>
    
    const handleClick = (event, type) => {
        console.log('type===>', type); // type===> first
    }
    

注意:经过屡次行内绑定on+事情发现,只履行了first(第一个绑定的事情),这是由于行内绑定on+事情(同一事情)只支撑绑定一个,当第一个现已绑定上,后续的同类型(同一个)事情就无法绑定上了)

  • js绑定事情(脚本模型) :经过JavaScript获取页面元素,然后运用元素的onclick等事情特点来绑定事情处理函数。
  • 单次绑定:
       <div id="demo">
          <button id="button">点我</button>
       </div>
    
       const buttonDom = document.getElementById("button");
       buttonDom.onclick = (event) => {
           console.log('event===> ', event); // event===> PointerEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0,…}
       }
    
  • 屡次绑定:
       const buttonDom = document.getElementById("button");
       buttonDom.onclick = (event) => {
           console.log('first===>', event); // 并没有履行
       }
       // 只会履行这个
       buttonDom.onclick = (event) => {
           console.log('last===>', event); // last===> PointerEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0,…}
       }
    

前端面试 第二篇  js 事情

经过屡次js绑定事发现,只履行了last(最终一个绑定的事情),是由浏览器完成的机制决定的;具有掩盖性,后者会掩盖前者

DOM2 事情模型

概念:是用于处理Web页面中事情的一种规范化模型(完成事情处理;引入了事情流的概念)。

其供给了关键的三个办法来处理和管理事情:

addEventListener

addEventListener:向指定方针增加事情监听器;

  • 参数:
    1. type:表明要监听的事情类型,比方'click''mouseover'等。

    2. listener:表明要触发的事情处理函数,也能够是一个完成了EventListener接口的方针,比方一个完成了handleEvent办法的方针。

    3. options(可选):一个包含有关事情监听的装备信息的可选方针。常见的装备选项包含:

      • capture:一个布尔值,指定事情是否在捕获阶段进行处理,默以为false
      • once:一个布尔值,指定事情是否只能被触发一次,默以为false
      • passive:一个布尔值,指定事情处理程序是否不会调用preventDefault()办法,默以为false
  • 特色:
    • 屡次绑定:答应为同一个事情类型的方针增加多个事情监听器,而不会掩盖之前的监听器;
    • 更灵敏的操控:供给了更灵敏的操控事情监听器的增加和移除,能够指定是否在捕获阶段冒泡阶段触发事情;
    • 事情托付: 经过事情托付的办法,能够在父元素上监听子元素的事情削减事情处理函数的数量进步功能;
    • 符合现在规范: 现代引荐的事情绑定办法,比较于传统的事情处理办法(如element.onclick),具有更好的兼容性可维护性;
    • 支撑不同类型的事情: 能够为不同类型的事情(如鼠标事情、键盘事情、表单事情等)增加监听器,完成更丰富的交互功能;

addEventListener

  • 屡次绑定
       <div id="demo">
           <button id="button">点我</button>
           <div id="two">1</div>
       </div>
    
       //屡次绑定
       const buttonDom = document.getElementById("button");
       const numDom = document.getElementById("two");
       buttonDom.addEventListener('click', (event) => {
           numDom.innerText = Number(numDom.innerText) + 1;
           console.log('first', new Date());
       })
       buttonDom.addEventListener('click', (event) => {
           numDom.innerText = Number(numDom.innerText) + 1;
           console.log('last', new Date());
       })
    

前端面试 第二篇  js 事情

以上代码能够看出 addEventListener 屡次绑定click事情都能够绑定上并且按照次序履行了;

  • 更灵敏的操控(操控是在捕获阶段还是冒泡阶段)
    #parent {
        display: flex;
        align-items: center;
        justify-content: center;
        background: red;
        height: 200px;
        width: 200px;
    }
    #target {
        background: white;
        height: 50px;
        width: 50px;
        border: none;
    }
    
    <div id="demo">
        <div id="parent">
            <button  id="target">点我</button> 
        </div>
    </div>
    
    • 操控在冒泡阶段(capture:true)
    const parentDom = document.getElementById("parent");
    const targetDom = document.getElementById("target");
    const handleClick = (type) =>{
        console.log('type===>', type)
    }
    parentDom.addEventListener('click', () => handleClick('parent'), {capture: true})
    // DOM0中无事情流的概念(能够认定为在冒泡阶段)
    targetDom.onclick = () => {
        handleClick('target')
    }
    

前端面试 第二篇  js 事情
捕获阶段便是从外出元素到方针元素;parent就在target的外层,所以捕获阶段parent绑定的办法先履行,然后再履行taregt绑定的办法

  • 操控在捕获阶段(capture:false)
     const parentDom = document.getElementById("parent");
     const targetDom = document.getElementById("target");
     const handleClick = (type) =>{
         console.log('type===>', type)
     }
     parentDom.addEventListener('click', () => handleClick('parent'), {capture: false})
     // DOM0中无事情流的概念(能够认定为在冒泡阶段)
     targetDom.onclick = () => {
         handleClick('target')
     }

前端面试 第二篇  js 事情
冒泡阶段便是从方针元素到最外层;target便是方针元素,所以冒泡阶段taregt绑定的办法先履行,然后再履行parent绑定的办法

  • 事情托付(事情署理)
    <div id="demo">
        <ul id="parent">
            <li>第一个</li>
            <li>第二个</li>
            <li>第三个</li>
            <li>第四个</li>
            <li>最终一个</li>
        </ul>
    </div>
    
    //事情托付
    const parentElement = document.getElementById("parent");
    parentElement.addEventListener('click', (event) => {
        console.log('我是parent的办法', event);
    })
    

前端面试 第二篇  js 事情
当li被点击时,由于冒泡原理,事情就会冒泡到ul上,由于ul上有点击事情,所以事情就会触发

假如想要把ul作为方针元素的话怎么做呢? 那是不是阻止事情流持续向下捕获就能够了?

stopPropagation:阻止事情流进程中事情持续向下捕获

const parentElement = document.getElementById("parent");
const firstElement = document.getElementById("first");
parentElement.addEventListener('click', (event) => {
    console.log('我是parent的办法', event);
    event.stopPropagation();
    return false;
},{capture: true}) // capture : true  捕获阶段
firstElement.addEventListener('click', (event) => {
    console.log('我是first的办法', event);
})

前端面试 第二篇  js 事情
当li被点击时,由于先进行事情捕获阶段,从ul => li捕获进程,事情方针供给了stopPropagation中止捕获持续向下找寻的办法,阻止了事情捕获

removeEventListener

removeEventListener:从指定方针中移除事情监听器(办法用于移除之前运用addEventListener办法增加的事情监听器)

语法:此处不多叙说, 请参阅EventTarget.removeEventListener

target.removeEventListener(type, listener); // 此处不多叙说,参阅MDN文档 

简单)举例:

  <div id="demo">
    <button id="button">点我</button>
 </div>
    const buttonDom = document.getElementById("button");
    const handleClick = (event) => {
        console.log(event);
        setTimeout(()=> {
            buttonDom.removeEventListener('click',  handleClick)
            console.log('已履行移除事情程序!');
        },1000)
    }
    buttonDom.addEventListener('click', handleClick);

前端面试 第二篇  js 事情
需求传输参数怎么办?

试一试

   const buttonDom = document.getElementById("button");
    const handleClick = (event, type) => {
        console.log(event,type);
        setTimeout(()=> {
            buttonDom.removeEventListener('click' ,handleClick)
            console.log('已履行移除事情程序!');
        },1000)
    }
    buttonDom.addEventListener('click', (event) => handleClick(event, 'add'));

以上代码会发现问题:removeEventListener 并没有移除handleClick事情履行程序(屡次点击,日志有输出(buttonDom绑定的监听器还在))

这是由于:JavaScript 中的函数是方针,每次定义函数(特别是匿名函数)时,都会创立一个新的函数方针。因此,假如你运用匿名函数作为事情处理函数,无法运用removeEventListener来移除它,由于每次定义匿名函数时,都会得到一个全新的引证

想要增加参数或者匿名函数履行程序怎么办?

const buttonDom = document.getElementById("button");
// AbortSignal接口表明一个信号方针(signal object),它答应你经过AbortController`方针与 DOM 恳求(如 Fetch)进行通讯并在需求时将其间止
const abortController = new AbortController();
const abortSignal = abortController.signal;
// 增加事情监听器
buttonDom.addEventListener('click', (e) => {
    console.log('click:我被点名了!');
    setTimeout(() => {
        // 移除事情监听器
        abortController.abort();
        console.log('持续click 调查还在吗?');
    },3000)
}, { signal: abortSignal });

AbortSignal需求参阅:AbortSigna 解决addEventListener绑定匿名函数 | 传输参数 removeEventListener 无法移除监听器问题;

dispatchEvent

概念: 用于在指定的元素上触发一个事情(能够模仿用户行为或触发自定义事情

举例:模仿点击完成主动登录(能够自己写个脚本去某些网站主动报到:比方报到(需求了解一下谷歌插件开发))

<div class="app">
        <section class="login">
            <form id="form">
                <input type="account " name="account " id="account" name="account" placeholder="请输入账号"/>
                <input type="password" name="password" id="password"  placeholder="暗码"/>
            </form>
            <button id="submit" class="submit" onclick="submit()" >提交</button>
        </section>
    </div>
// 假定账号暗码
    const  defaultCccount = '123456';
    const  defaultPassword = '123456';
    const submit = () => {
        const form = document.getElementById('form');
        const account  = form.elements['account'].value;
        const password = form.elements['password'].value;
        if(!account || !account.length) {
            alert('请输入账号!')
            return 
        }
        if(!password || !password.length) {
            alert('请输入暗码!')
            return 
        }
        if(defaultCccount === account && defaultPassword === password) {
            alert('登录成功!')
        } else {
            alert('账号或暗码过错!')
        }
    }
 // 浏览器操控台
 window.account.value = '123456';
 window.password.value = '123456';
 window.submit.dispatchEvent(clickEvent);

前端面试 第二篇  js 事情

IE 事情模型

概念:IE事情模型是Internet Explorer浏览器特有的事情处理机制(事情流只要两个阶段:方针阶段和冒泡阶段;在冒泡阶段,事情会从方针元素开端,然后向上冒泡至父元素,直至到达根元素)

事情模型
类型 DOM0事情模型 DOM2事情模型 IE事情模型
特色 是最早的事情处理机制,直接在HTML元素上定义事情处理函数,如经过onclickonload等特点来绑定事情 是W3C规范模型,更强大和灵敏的事情处理机制;
* 它运用addEventListener办法来绑定事情处理函数;
* 支撑事情传达(包含捕获阶段、方针阶段和冒泡阶段)
是Internet Explorer浏览器特有的事情处理机制,运用attachEventdetachEvent办法进行事情的绑定和免除。
长处 简单直观,易于了解和运用,一切浏览器都支撑 * 支撑为同一事情类型绑定多个处理函数;
* 答应在捕获或冒泡阶段处理事情;
* 供给了撤销事情默许行为和阻止事情传达的机制
在IE浏览器中直接运用,无需额定的兼容性处理
缺点 * 不支撑事情传达(没有捕获和冒泡阶段);
* 无法为同一事情类型绑定多个处理函数,无法撤销事情的默许行为或阻止事情传达。
在一些老版别的浏览器(如IE6-8)中或许不被支撑 * 不支撑规范的事情传达模型(没有捕获阶段);
* 不支撑为同一事情类型绑定多个处理函数;
* 事情处理函数的this指向window而不是触发事情的元素,需求经过全局变量event来拜访事情方针;
* IE事情模型的应用规模逐步缩小(逐步退出舞台)

提出一个疑问(问题?)

  • 一个button 即HTML行内形式绑定onclick 一起经过脚本onClick绑定事情, 又经过 addEventListener 绑定click事情后,哪一个先履行? 为什么?

      <div id="demo">
       <button onclick="handleClick(event)" id="button">点我</button>
      </div>
    
    const buttonDom = document.getElementById("button");
    const handleClick = (event) => { 
        console.log('我是行内', event.target);  // 不会履行
    }
    buttonDom.addEventListener('click', (event) => {
        console.log('我是监听器', event.target); // 最终履行
    });
    buttonDom.onclick = (event) => {
        console.log('我是js脚本', event.target);  // 第一个履行
    }
    

前端面试 第二篇  js 事情

  • 以上能够看出 第一个履行的是 js脚本绑定的时刻程序,其次是addEventListener绑定的时刻程序履行,行内的没有履行;

  • 行内事情处理器(比方直接在HTML元素上设置的onclick特点)通常具有比经过addEventListener增加的事情监听器更高的优先级,这是由于行内处理器是元素特点的一部分,它们在DOM解析进程中就被绑定到元素【随后经过buttonDom.onclick(这实际上也是设置行内处理器的一种办法)掩盖了本来的行内处理器handleClick,但由于浏览器处理行内处理器的特殊机制,原本经过onclick特点设置的行内处理器(虽然被掩盖了)仍然会首先履行】

  • 行内绑定的事情被js脚本绑定的事情掩盖了;当一个元素一起运用行内形式绑定onclick事情和经过脚本绑定onClick事情时,脚本绑定的事情会掩盖行内形式绑定的事情。这是由于脚本绑定的事情会在页面加载后动态地替换行内形式绑定的事情,从而掩盖了之前的事情处理函数

  • 经过addEventListener绑定的click事情会在捕获或冒泡阶段被履行,取决于addEventListener的第三个参数(默许是在冒泡阶段履行)

onclick 与 addEventListener html <div id="demo"> <button id="button">点我</button> </div>

```js
const buttonDom = document.getElementById("button");
buttonDom.addEventListener('click', (event) => {
    console.log('我是监听器', event.target); // 第一个履行
});
buttonDom.onclick = (event) => {
    console.log('我是js脚本', event.target);  //最终一个履行
}
```

前端面试 第二篇  js 事情

onclick(冒泡阶段履行) 与 addEventListener 履行次序与绑定次序和 addEventListener第三个参数(履行阶段有关)

———————————————–END—————————————————-
个人总结以及了解,有误欢迎大佬指正;

前端面试 第二篇  js 事情