丸子學(xué)JS(學(xué)習(xí)2小時(shí) - 事件處理)

事件

  • addEventListener 用來(lái)處理多個(gè)事件同時(shí)綁定
 element.addEventListener(event, handler[, options]);

handler 事件處理程序, event 事件對(duì)象,handleEvent 對(duì)象處理程序

冒泡和捕獲

  • 冒泡

當(dāng)一個(gè)事件發(fā)生在一個(gè)元素上,它會(huì)首先運(yùn)行在該元素上的處理程序,然后運(yùn)行其父元素上的處理程序,然后一直向上到其他祖先上的處理程序

  • event.target

引發(fā)事件的那個(gè)嵌套層級(jí)最深的元素被稱為目標(biāo)元素,可以通過(guò) event.target 訪問(wèn)

停止冒泡
event.stopPropagation()用于停止冒泡

event.stopImmediatePropagation() 用于停止冒泡,并阻止當(dāng)前元素上的處理程序運(yùn)行。使用該方法之后,其他處理程序就不會(huì)被執(zhí)行

捕獲
DOM事件標(biāo)準(zhǔn)描述了事件傳播的 3 個(gè)階段:

  1. 捕獲階段(Capturing phase)—— 事件(從 Window)向下走近元素。
  2. 目標(biāo)階段(Target phase)—— 事件到達(dá)目標(biāo)元素。
  3. 冒泡階段(Bubbling phase)—— 事件從元素上開(kāi)始冒泡。
    如果要在捕獲階段捕獲事件 ,需要將處理程序的 capture選項(xiàng)設(shè)置為 true
ele.addEventListener(..., {capture: true})
ele.addEventListener(..., true)

事件委托

如果我們有許多以類似方式處理的元素,那么就不必為每個(gè)元素分配一個(gè)處理程序 —— 而是將單個(gè)處理程序放在它們的共同祖先上

  • 行為模式
  1. 行為: 計(jì)數(shù)器
var btn = document.querySelector("button");
var num = 0;
btn.onclick = function (){
  btn.innerHTML = ++num;
  if(btn.innerText == 10){
    alert("不累么??")
    btn.style.background = "orange";
  }
}
  1. 行為:切換器
<div class="wrapper">
    <button data-toggle-id="changeBox">點(diǎn)擊切換</button>
    <div id="changeBox"></div>

</div>
<script>
    let btn = document.querySelector('button')
    btn.addEventListener('click', function(event){
        let id = event.target.dataset.toggleId;
        if(!id) return 
        let ele = document.querySelector('#'+id)
        ele.style.display == 'none' ?
        ele.style.display = 'block':
        ele.style.display = 'none'
    })
</script>

瀏覽器默認(rèn)行為

event.preventDefault() 用于阻止默認(rèn)行為,如果處理程序是使用 on<event>(而不是 addEventListener)分配的,那返回 false 也同樣有效

創(chuàng)建自定義事件

  • 事件構(gòu)造器
let event = new Event(type[,options])

type —— 事件類型,可以是像這樣 "click" 的字符串,或者我們自己的像這樣 "my-event" 的參數(shù)
options —— 具有兩個(gè)可選屬性的對(duì)象:默認(rèn)情況下,以上兩者都為 false:{bubbles: false, cancelable: false}

  • 觸發(fā)
    通過(guò)使用 elem.dispatchEvent(event)調(diào)用在元素上“運(yùn)行”
  • 其他一些事件
    ● UIEvent
    ● FocusEvent
    ● MouseEvent
    ● WheelEvent
    ● KeyboardEvent
let event = new MouseEvent("click", {
  bubbles: true,
  cancelable: true,
  clientX: 100,
  clientY: 100
});
  • 自定義事件
<h1 id="elem">Hello for John!</h1>

<script>
  // 事件附帶給處理程序的其他詳細(xì)信息
  elem.addEventListener("hello", function(event) {
    alert(event.detail.name);
  });

  elem.dispatchEvent(new CustomEvent("hello", {
    detail: { name: "John" }
  }));
</script>

鼠標(biāo)事件

  • 鼠標(biāo)事件類型

mousedown / mouseup
在元素上點(diǎn)擊/釋放鼠標(biāo)按鈕

mouseover / mouseout
鼠標(biāo)指針從一個(gè)元素上移入/移出

mousemove
鼠標(biāo)在元素上的每個(gè)移動(dòng)都會(huì)觸發(fā)此事件

click
果使用的是鼠標(biāo)左鍵,則在同一個(gè)元素上的 mousedown 及 mouseup 相繼觸發(fā)后,觸發(fā)該事件

dblclick
在短時(shí)間內(nèi)雙擊同一元素后觸發(fā)

contextmenu
在鼠標(biāo)右鍵被按下時(shí)觸發(fā)

  • 事件順序
    在單個(gè)動(dòng)作觸發(fā)多個(gè)事件時(shí), 事件的順序是固定的,會(huì)遵循 mousedown -> mouseup -> click 的順序調(diào)用處理程序

  • 鼠標(biāo)按鈕
    使用 button 屬性來(lái)區(qū)分是左鍵單擊還是右鍵單擊

  • 組合鍵: shift、alt、ctrl、meta
    事件屬性:
    ● shiftKey:Shift
    ● altKey:Alt(或?qū)τ?Mac 是 Opt)
    ● ctrlKey:Ctrl
    ● metaKey:對(duì)于 Mac 是 Cmd

  • 坐標(biāo): clientX/Y, pageX/Y

  1. 相對(duì)于窗口的坐標(biāo):clientX 和 clientY。
  2. 相對(duì)于文檔的坐標(biāo):pageX 和 pageY
  • 防止在鼠標(biāo)按下時(shí)的選擇
    防止瀏覽器對(duì) mousedown進(jìn)行操作,可以阻止鼠標(biāo)按下時(shí)的選擇
Before...
<b ondblclick="alert('Click!')" onmousedown="return false">
  Double-click me
</b>
...After

通過(guò)使用oncopy事件, 可以防止用戶復(fù)制

<div oncopy="alert('Copying forbidden!');return false">
  Dear user,
  The copying is forbidden for you.
  If you know JS or HTML, then you can get everything from the page source though.
</div>

移動(dòng)鼠標(biāo): mouseover/out , mouseenter/leave

  • 事件mouseover/mouseout, relatedTarget
    當(dāng)鼠標(biāo)指針移到某個(gè)元素上時(shí),mouseover 事件就會(huì)發(fā)生,而當(dāng)鼠標(biāo)離開(kāi)該元素時(shí),mouseout 事件就會(huì)發(fā)生

    對(duì)于mouseover:
    ● event.target —— 是鼠標(biāo)移過(guò)的那個(gè)元素。
    ● event.relatedTarget —— 是鼠標(biāo)來(lái)自的那個(gè)元素(relatedTarget → target)
  • 跳過(guò)元素

    注意: 如果 mouseover被觸發(fā)了, 則必須有 mouseout
  • 事件 mouseenter和mouseleave
    事件 mouseenter/mouseleave 類似于 mouseover/mouseout。它們?cè)谑髽?biāo)指針進(jìn)入/離開(kāi)元素時(shí)觸發(fā)
  1. 元素內(nèi)部與后代之間的轉(zhuǎn)換不會(huì)產(chǎn)生影響。
  2. 事件 mouseenter/mouseleave 不會(huì)冒泡。

鼠標(biāo)拖放事件

  • 拖放算法
  1. 在 mousedown 上 —— 根據(jù)需要準(zhǔn)備要移動(dòng)的元素(也許創(chuàng)建一個(gè)它的副本,向其中添加一個(gè)類或其他任何東西)
  2. 然后在 mousemove 上,通過(guò)更改 position:absolute 情況下的 left/top 來(lái)移動(dòng)它
  3. 在 mouseup 上 —— 執(zhí)行與完成的拖放相關(guān)的所有行為
ball.onmousedown = function(event) {
  // (1) 準(zhǔn)備移動(dòng):確保 absolute,并通過(guò)設(shè)置 z-index 以確保球在頂部
  ball.style.position = 'absolute';
  ball.style.zIndex = 1000;

  // 將其從當(dāng)前父元素中直接移動(dòng)到 body 中
  // 以使其定位是相對(duì)于 body 的
  document.body.append(ball);

  // 現(xiàn)在球的中心在 (pageX, pageY) 坐標(biāo)上
  function moveAt(pageX, pageY) {
    ball.style.left = pageX - ball.offsetWidth / 2 + 'px';
    ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
  }

  // 將我們絕對(duì)定位的球移到指針下方
  moveAt(event.pageX, event.pageY);

  function onMouseMove(event) {
    moveAt(event.pageX, event.pageY);
  }

  // (2) 在 mousemove 事件上移動(dòng)球
  document.addEventListener('mousemove', onMouseMove);

  // (3) 放下球,并移除不需要的處理程序
  ball.onmouseup = function() {
    document.removeEventListener('mousemove', onMouseMove);
    ball.onmouseup = null;
  };
};

為防止瀏覽器自己的拖放處理與我們的拖放處理產(chǎn)生沖突,需禁用它

ball.ondragstart = function() {
  return false;
};
  • 修正定位
ball.onmousedown = function(event) {

  let shiftX = event.clientX - ball.getBoundingClientRect().left;
  let shiftY = event.clientY - ball.getBoundingClientRect().top;

  ball.style.position = 'absolute';
  ball.style.zIndex = 1000;
  document.body.append(ball);

  moveAt(event.pageX, event.pageY);

  // 移動(dòng)現(xiàn)在位于坐標(biāo) (pageX, pageY) 上的球
  // 將初始的偏移考慮在內(nèi)
  function moveAt(pageX, pageY) {
    ball.style.left = pageX - shiftX + 'px';
    ball.style.top = pageY - shiftY + 'px';
  }

  function onMouseMove(event) {
    moveAt(event.pageX, event.pageY);
  }

  // 在 mousemove 事件上移動(dòng)球
  document.addEventListener('mousemove', onMouseMove);

  // 放下球,并移除不需要的處理程序
  ball.onmouseup = function() {
    document.removeEventListener('mousemove', onMouseMove);
    ball.onmouseup = null;
  };
};

ball.ondragstart = function() {
  return false;
};
  • 潛在的放置目標(biāo)
    有一個(gè)叫做 document.elementFromPoint(clientX, clientY) 的方法。它會(huì)返回在給定的窗口相對(duì)坐標(biāo)處的嵌套的最深的元素(如果給定的坐標(biāo)在窗口外,則返回 null)
    基于 onMouseMove 擴(kuò)展的代碼,用于查找 “droppable” 的元素
// 我們當(dāng)前正在飛過(guò)的潛在的 droppable 的元素
let currentDroppable = null;

function onMouseMove(event) {
  moveAt(event.pageX, event.pageY);

  ball.hidden = true;
  let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
  ball.hidden = false;

  // mousemove 事件可能會(huì)在窗口外被觸發(fā)(當(dāng)球被拖出屏幕時(shí))
  // 如果 clientX/clientY 在窗口外,那么 elementfromPoint 會(huì)返回 null
  if (!elemBelow) return;

  // 潛在的 droppable 的元素被使用 "droppable" 類進(jìn)行標(biāo)記(也可以是其他邏輯)
  let droppableBelow = elemBelow.closest('.droppable');

  if (currentDroppable != droppableBelow) {
    // 我們正在飛入或飛出...
    // 注意:它們兩個(gè)的值都可能為 null
    //   currentDroppable=null —— 如果我們?cè)诖耸录?,鼠?biāo)指針不是在一個(gè) droppable 的元素上(例如空白處)
    //   droppableBelow=null —— 如果現(xiàn)在,在當(dāng)前事件中,我們的鼠標(biāo)指針不是在一個(gè) droppable 的元素上

    if (currentDroppable) {
      // 處理“飛出” droppable 的元素時(shí)的處理邏輯(移除高亮)
      leaveDroppable(currentDroppable);
    }
    currentDroppable = droppableBelow;
    if (currentDroppable) {
      // 處理“飛入” droppable 的元素時(shí)的邏輯
      enterDroppable(currentDroppable);
    }
  }
}

鍵盤: keydown 和 keyup

當(dāng)一個(gè)按鍵被按下時(shí),會(huì)觸發(fā)keydown事件,而當(dāng)按鍵被釋放時(shí),會(huì)觸發(fā)keyup事件。
注意: event.codeevent.key
事件對(duì)象的 key 屬性允許獲取字符,而事件對(duì)象的 code 屬性則允許獲取“物理按鍵代碼”

滾動(dòng)

  • 防止?jié)L動(dòng)
    只能在 pageUp和pageDown的 keydown事件上, 使用event.preventDefault() 來(lái)阻止?jié)L動(dòng)

表單屬性和方法

  • 導(dǎo)航: 表單和元素
    文檔中的表單是特殊集合 document.forms 的成員
  • input和textarea
    通過(guò) input.value(字符串)或 input.checked(布爾值)來(lái)訪問(wèn)復(fù)選框(checkbox)中的它們的 value

聚焦: focus / blur

  • focus/blur 事件
    當(dāng)元素聚焦時(shí),會(huì)觸發(fā)focus事件,當(dāng)元素失去焦點(diǎn)時(shí),會(huì)觸發(fā)blur事件
  • focus/blur方法
    elem.focus()elem.blur() 方法可以設(shè)置和移除元素上的焦點(diǎn)
  • 允許在任何元素上聚焦: tabindex
點(diǎn)擊第一項(xiàng),然后按 Tab 鍵。跟蹤順序。請(qǐng)注意,多按幾次 Tab 鍵后,會(huì)將焦點(diǎn)移到這個(gè)通過(guò) iframe 嵌入的示例的外面。
<ul>
  <li tabindex="1">One</li>
  <li tabindex="0">Zero</li>
  <li tabindex="2">Two</li>
  <li tabindex="-1">Minus one</li>
</ul>

<style>
  li { cursor: pointer; }
  :focus { outline: 1px dashed green; }
</style>
  • focus/blur 委托
    focus 和 blur 事件不會(huì)向上冒泡。
    使用 focusin 和 focusout 事件 —— 與 focus/blur 事件完全一樣,只是它們會(huì)冒泡。
    值得注意的是,必須使用 elem.addEventListener 來(lái)分配它們,而不是 on<event>

表單事件: change, input, cut, copy, paste

  • 事件: change
    當(dāng)元素更改完成時(shí), 將觸發(fā)change事件
<input type="text" onchange="alert(this.value)">
  • 事件: input
    每當(dāng)用戶對(duì)輸入值進(jìn)行修改后, 就會(huì)觸發(fā)input 事件
  • 事件: cut、copy、paste
    這些事件發(fā)生于剪切/拷貝/粘貼一個(gè)值的時(shí)候
    它們屬于 ClipboardEvent類, 并提供了對(duì)剪切/拷貝/粘貼的數(shù)據(jù)的訪問(wèn)方法
<input type="text" id="input">
<script>
  input.onpaste = function(event) {
    alert("paste: " + event.clipboardData.getData('text/plain'));
    event.preventDefault();
  };

  input.oncut = input.oncopy = function(event) {
    alert(event.type + '-' + document.getSelection());
    event.preventDefault();
  };
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容