JS 實作 #9 | 刪除預算按鈕的事件處理;Event Delegation 事件委派 Node.parentNode()、Node.childNodes()

在預算小程式中,可以針對已輸入的預算筆數進行刪除,如下圖右邊的 x 按鈕。在這頁中,透過 JS 的 event delegation 特性來完成每一個預算筆數刪除按鈕的事件委派。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5f2f458d-6b15-44bf-9bbc-1fb9a70b4e77/_2020-03-31_17.24.09.jpg

Event Delegation

Event Delegation (事件委派) 是一種受惠於 Event Bubbling 而能減少監聽器數目的方法。

使用情境

  1. 當我們有一個 element ,底下有很多個 child elements 都有同樣的事件動作時,我們不會想要一個一個委派 event handler。而是透過 Event Delegation 一次整個委派。
  2. 當我們想委派的 DOM 元件,一開始根本就不存在於 DOM tree 中。這個時候也是透過 Event Delegation 委派。

範例程式碼

<div class="parent">
    <div class="child" data-name="a"></div>
    <div class="child" data-name="b"></div>
    <div class="child" data-name="c"></div>
    <div class="subitem" data-name="d"></div>
</div>

$('.parent').on('click', '.child', function(){
    console.log($(this).data('name'));   
});

DOM Traverse:DOM 節點間的查找遍歷(Traversing)

由於 DOM 節點有分層的概念,於是節點與節點之間的關係,我們大致上可以分成兩種:

  • 父子關係:除了 document 之外,每一個節點都會有個上層的節點,我們通常稱之為「父節點」 (Parent node),而相對地,從屬於自己下層的節點,就會稱為「子節點」(Child node)。 Node.childNodes()Node.firstChild()Node.lastChild()Node.parentNode()
  • 兄弟關係:有同一個「父節點」的節點,那麼他們彼此之間就是「兄弟節點」(Siblings node)。 Node.previousSibling()Node.nextSibling()

開始實作

1. 由於 income、expense 二種預算都可以進行刪除,因為透過 Event Delegation 特性,找到最上層的 <div class="container"> 來選擇,並且指派 click 事件對應到新增的內部方法 ctrlDeleteItem()

// UI CONTROLLER
var UIController = (function() {

    var DOMstrings = {
        inputType: '.add__type',
        inputDescription: '.add__description',
        inputValue: '.add__value',
        inputBtn: '.add__btn',
        incomeContainer: '.income__list',
        expenseContainer: '.expenses__list',
        budgetLabel: '.budget__value',
        incomeLabel: '.budget__income--value',
        expensesLabel: '.budget__expenses--value',
        percentageLabel: '.budget__expenses--percentage',
        **container: '.container'**
    };

    return {
       ...
    };

})();
// GLOBAL APP CONTROLLER
var controller = (function(budgetCtrl, UICtrl){

    var setupEventListeners = function() {
        var DOM = UICtrl.getDOMstrings();
        document.querySelector(DOM.inputBtn).addEventListener('click', ctrlAddItem);
        document.addEventListener('keypress', function(event) {
            if (event.keyCode === 13 || event.which === 13) {
                ctrlAddItem();
            }
        });
        
        **document.querySelector(DOM.container).addEventListener('click', ctrlDeleteItem);**

    };
    var updateBudget = function() {
       ...
    };

    var ctrlAddItem = function() {
       ...
    }

    **var ctrlDeleteItem = function(event) {
			 // to do
    }**
    
    return {
	     ...
    }
})(budgetController, UIController);

controller.init();

2. 透過 addEventListener 傳入變數 eventctrlDeleteItem,可以獲得目前 container 裡點擊的物件DOM。(注意由於 Event Delegation 特性,<div class="container"> 裡的所有 DOM 都被綁定了 click 事件。)

而此時我們要處理的是這個右側的 x 按鈕;點擊後刪除此筆記錄。

<div class="item clearfix" id="inc-1">
    <div class="item__description">Salary</div>
    <div class="right clearfix">
        <div class="item__value">+ 2,100.00</div>
        <div class="item__delete">
            <button class="item__delete--btn">
							**<i class="ion-ios-close-outline"></i>**
						</button>
        </div>
    </div>
</div>
// GLOBAL APP CONTROLLER
var controller = (function(budgetCtrl, UICtrl){

    var setupEventListeners = function() {
        var DOM = UICtrl.getDOMstrings();
        document.querySelector(DOM.inputBtn).addEventListener('click', ctrlAddItem);
        document.addEventListener('keypress', function(event) {
            if (event.keyCode === 13 || event.which === 13) {
                ctrlAddItem();
            }
        });
        
        document.querySelector(DOM.container).addEventListener('click', ctrlDeleteItem);

    };
    var updateBudget = function() {

        ...
    };

    var ctrlAddItem = function() {

        ...
        
    }

    **var ctrlDeleteItem = function(event) {
        var itemID, splitID, type, ID;
        itemID = event.target.parentNode.parentNode.parentNode.parentNode.id;
        if (itemID) {
            // event.target 獲得叉叉按鈕的 DOM 元件
            // 參考 HTML 四層 partentNode 將會指向到這筆記錄
            // .id 即獲得 inc-1
            // 將字串透過 split 處理,獲得各自部份
            splitID = itemID.split("-"); //['inc','1']
            type = splitID[0];  //'inc'
            ID = splitID[1];    //'1'

            // 1. delete the item from the data structure
            // 2. delete the item from the UI
            // 3. update and show the new budget
        }
    }**
    
    return {
        ...
    }
})(budgetController, UIController);

controller.init();

Zeen is a next generation WordPress theme. It’s powerful, beautifully designed and comes with everything you need to engage your visitors and increase conversions.