iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0
JavaScript

30天 JavaScript 提升計畫:從零到精通結合2024年的創新功能系列 第 21

第 21 天:JavaScript 的事件處理和他的 DOM

  • 分享至 

  • xImage
  •  

DOM


DOM(Document Object Model)操作文件物件模型,是的!繞來繞去還是物件,所以使用過程中不難發現都是用第 5 天:物件與陣列物件,通過對這些物件的屬性和方法進行操作來改變頁面內容或樣式。

DOM 的基本概念
如果將 DOM 轉換為圖表理解,會以類似「樹狀關係圖」的樣貌展現,因此常常聽見 DOM Tree 也代表 DOM 架構資料的樣貌,和 HTML 一樣有階層關係,不過使用物件來呈現。

DOM tree

圖片來源:DOM API 一篇就會!

DOM 的應用

  • 動態內容更新:
    利用 DOM,開發者可以在頁面加載後動態更新內容,比如顯示用戶提交的表單數據。

        <form id="myForm">
            <input type="text" id="userInput" placeholder="輸入一些內容" required >
            <button type="submit">提交</button>
        </form>
        <div id="output" style="margin: 1rem;" />
    
    /** 取 DOM */
    const form = document.getElementById('myForm');
    const output = document.getElementById('output');
    
    /** 事件 */
    form.addEventListener('submit', function (event) {
        // 阻止默認提交
        event.preventDefault();
        const userInput = document.getElementById('userInput').value;
    
        // 更新內容
        output.textContent = `你輸入的內容是:${userInput}`;
    });
    
  • 操作樣式:
    可以使用 JavaScript 修改元素的 CSS 樣式,以便實現動畫或其他視覺效果。

        <div id="myBox" />
        <button type="button" id="changeStyle">改變樣式</button>
    
            #myBox {
                width: 100px;
                height: 100px;
                background-color: blue;
                transition: all 0.5s;
            }
    
    /** 取 DOM */
    const box = document.getElementById('myBox');
    const button = document.getElementById('changeStyle');
    
    /** 事件 */
    button.addEventListener('click', function () {
        box.style.backgroundColor = 'red'; // 改變顏色
        box.style.width = '200px';         // 改變寬度
        box.style.height = '200px';        // 改變高度
    });
    
  • 增刪節點:
    可以動態增加或刪除 HTML 元素,例如從列表中移除一個項目。

        <ul id="itemList">
            <li>項目 1 <button type="button" class="remove">刪除</button></li>
            <li>項目 2 <button type="button" class="remove">刪除</button></li>
            <li>項目 3 <button type="button" class="remove">刪除</button></li>
        </ul>
    
    const itemList = document.getElementById('itemList');
    
    itemList.addEventListener('click', function (event) {
        if (event.target.classList.contains('remove')) {
            const listItem = event.target.parentElement;
            itemList.removeChild(listItem); // 刪除項目
        }
    });
    
  • 資料綁定:
    與框架(如 Vue、React 等)結合使用時,DOM 可以與數據模型綁定,自動更新界面。

    <script setup>
    import { ref } from 'vue'
    
    const message = ref('');
    </script>
    
    <template>
      <input v-model="message" placeholder="輸入消息" />
      <p>你輸入的消息是:{{ message }}</p>
    </template>
    

事件的基本概念和生命週期


基本概念
使用者瀏覽網頁的行為會觸發許多「事件」,像是按下按鍵、移動滑鼠、點擊網頁元素,JavaScript 讓前端能夠對這些事件進行監聽和響應。

常見的事件型態包含 click、change、mousemove 等等,可以參考 MDN Event reference 了解更多事件型態。

MDN 文件:Event reference

事件生命週期
觸發:用戶與元素互動,例如點擊按鈕。
捕獲:事件從 window 開始,向下傳遞到目標元素。
目標:事件到達目標元素,此時會執行與該事件相關的處理程序。
冒泡:事件從目標元素向上傳遞,直到 window。

事件監聽 addEventListener 和事件委託


addEventListener
addEventListener 方法用於將事件監聽器附加到指定的 DOM 元素。

target.addEventListener(event, handler, options);
  • event
    事件型態,註冊一個事件,讓系統監聽觸發互動效果;主要分成 2 種型別:
    1. keyboard event 鍵盤事件:
      • keydown 按下鍵盤觸發事件
      • keypress 只會針對可以輸出文字符號的按鍵有效( ESC、方向鍵、倒退鍵無效)
      • keyup 放開鍵盤的那個剎那
    2. mouse event 滑鼠事件:
      • click:滑鼠點擊 HTML 元素時會觸發
      • change:當表單內容改變時會觸發
      • scroll:滾動時觸發
      • dblclick:滑鼠聯繫 2 次點擊 HTML 元素
      • mouseover
  • handler:觸發事件時所執行的函式。
  • options:選擇性加入一些額外的事件監聽設定。
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
    alert('Button clicked!');
});

🔔 在指定元素(domElement)監聽特定事件(此範例為 click 事件,需使用引號),當該事件觸發時會執行後方函式,函式有一個參數為事件物件

事件委託
事件委託是將事件監聽器附加到父元素上,而不是每個子元素。這樣可以提高性能,特別是當子元素動態生成時。

<ul id="myList">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>
const list = document.getElementById('myList');
list.addEventListener('click', function(event) {
    if (event.target.tagName === 'LI') {
        alert(`You clicked on ${event.target.textContent}`);
    }
});

事件物件和事件處理函數的使用


事件物件
當事件發生時,JavaScript 會創建一個事件物件,包含有關事件的信息,如:

  • type:事件類型,例如 click
  • target:事件的目標元素。
  • currentTarget:當前事件處理函數所綁定的元素。
  • preventDefault():阻止事件的默認行為。
    anchorElement.addEventListener("click", function (event) {
      // 阻止連結導航
      event.preventDefault();
    });
    
  • stopPropagation():停止事件的捕獲或冒泡。

事件處理函數
事件處理函數是響應事件的函數。它通常接收一個事件物件作為參數。

const input = document.getElementById('myInput');
input.addEventListener('input', function(event) {
    console.log('Input value:', event.target.value);
});

常用的 DOM 方法


// 操縱屬性
element.setAttribute("attributeName", "value"); element.getAttribute("attributeName");
element.removeAttribute("attributeName");

// 修改內容
element.innerHTML = "New content";
element.textContent = "New text content";

// 操縱 class
element.classList.add("className");
element.classList.remove("className");
element.classList.toggle("className");

// Event Handling 事件處理
element.addEventListener("click", function);
element.removeEventListener("click", function);

// Window 方法
window.alert("Message"); 
window.prompt("Prompt message", "default value");
window.confirm("Confirmation message");

前端框架 Vue 使用 ref 取得 DOM


Vue 提供了 ref 這個 instance property 的來解決上述問題;只要在透過 ref 這個 attribute 就可以讓 Vue 定位取得你想要的 instance;簡單來說,ref 屬性是可以快速取得 DOM 元素所使用。

💡 補充說明:同樣是 Vue 提供的 ref<template><script setup> 有什麼不一樣?
<template>:使用的 ref 是一個特殊的屬性,用於獲取對 DOM 元素或子組件的直接引用。
<script setup>:響應式的 ref 用於創建響應式數據。在 Vue3 中,使用 ref 函數可以將基本類型的值(如字符串、數字)轉換為響應式的引用。


上一篇
第 20 天:JavaScript 工具鏈 - 包管理
下一篇
第 22 天:JavaScript 的正規表達式
系列文
30天 JavaScript 提升計畫:從零到精通結合2024年的創新功能30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
kuku
iT邦新手 4 級 ‧ 2024-09-28 15:35:14

最近剛好實戰上遇到一個很經典的例子可以分享:表格要做拖曳,當然拖曳過程要新增模擬線,讓使用者知道即將拖曳到哪列,也就是提升使用者互動體感!

前面的拖曳不難,重點在不能使用套件要新增模擬線!🙃

透過vue-drag-and-drop知道有現成的 API 可以用,但是...怎麼 console.log 結果都是 DragEvent console.log

本規劃有機會可以抓到 row_index 這樣就方便算進入第幾列然後給固定高度計算給線,問題來了,怎麼從這些訊息中抓到 row_index?

曾經有夥伴跟我說:
你不會給每行都一個 id 唷!心理 os,我抓得到 id 還用煩惱嗎?
不然計算全畫面高度在去 ++--...,心理 os,天呀!饒了我吧!

所以最後解法是用 DOM 抓!

const getRowIndex = (event) => {
  const rowElement = event.target.closest('.ivu-table-row');
  if (!rowElement) return null

  return Array.from(rowElement.parentNode.children).indexOf(rowElement)
}

畫面

我要留言

立即登入留言