iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
Modern Web

JS 忍者訓練計畫系列 第 27

從事件中倖存(下) Day26

  • 分享至 

  • xImage
  •  

除了基本的綁定事件與取消綁定事件基礎,還有許多管理事件的技巧。除打造相互獨立的模組化元件,還有委託事件與氣泡機制等需要注意或可用的方式,另外這一章沒特別深入非同步事件的處理,以及自訂事件等,都是直得慢慢研究跟學習的地方。

這章想學到什麼?

  • 中央集中存放相關連資訊
  • 綁定事件處理者並具有追蹤能力的函式
  • 氣泡機制複習
  • 委託機制複習

程式碼閱讀練習與撰寫

中央集中存放相關連資訊

(function(){

    var cache = {},
        guidCounter = 1,
        expando = "data" + (new Date).getTime();
        
    this.getData = function (elem) {
        var guid = elem[expando];
        if(!guid) {
            guid = elem[expando] = guidCounter++;
            cache[guid] = {};
        }
        return cache[guid]
    }
    
    this.removeData = function (elem) {
        var guid = elem[expando];
        if (!guid) return;
        delete cache[guid];
        try {
            delete elem[expando]
        }
        catch (e) {
            if(elem.removeAttribute){
                elem.removeAttribute(expando);
            }
        }
    }

})();

var elems = document.getElementsByTagName('div');

for(var n = 0; n < elems.length;n++) {
    getData(elems[n]).ninja = elems[n].className;;
}

for(var n = 0; n < elems.length;n++) {
    console.log(getData(elems[n]).ninja)
}

綁定事件處理者並具有追蹤能力的函式

(function(){

var nextGuid = 1;

this.addEvent = function (elem, type, fn) {
    var data = getData(elem);
    
    if(!data.handlers) data.handlers = {};
    
    if(!data.handlers[type])
    data.handlers[type] = [];
    
    if(!fn.guid) fn.guid = nextGuid++;
    
    data.handlers[type].push(fn);
    
    if(!data.dispatcher) {
        data.disabled = false;
        data.dispatcher = function (event) {
            if(data.disabled) return;
            event = fixEvent(event);
            
            var handlers = data.handlers[event.type];
            if(handlers) {
                for (var n = 0; n < handlers.length;n++) {
                handlers[n].call(elem, event);
                }
            }
        }
    }
    
    if(!data.handlers[type].length == 1) {
        if(document.addEventListener) {
            elem.addEventListener(type, data.dispatcher, false)
        }
        else if (document.attachEvent) {
            elem.attachEvent("on" + type, data.dispatcher);
        }
    }
}
})()

氣泡機制複習

//HTML
<div>1
    <div>2
        <div>3
            <div>4
                <div>5</div>
            </div>
        </div>
    </div>
</div>
<button id="clear">clear output</button>
<section id="log"></section>

//CSS
p {
  line-height: 0;
}

div {
  display: inline-block;
  padding: 5px;

  background: #fff;
  border: 1px solid #aaa;
  cursor: pointer;
}

div:hover {
  border: 1px solid #faa;
  background: #fdd;
}

//JS
/*
 * source 1: https://dom.spec.whatwg.org/#dom-event-eventphase
 * source 2: https://stackoverflow.com/a/4616720/15266715
*/
const evtPhasestr = ["NONE: ", "CAPTURING_PHASE: ", "AT_TARGET: ", "BUBBLING_PHASE: "];
const logElement = document.getElementById('log');

function log(msg) {
    logElement.innerHTML += (`<p>${msg}</p>`);
}

function phase(evt) {
    log(evtPhasestr[evt.eventPhase] + this.firstChild.nodeValue.trim());
}
function gphase(evt) {
    log(evtPhasestr[evt.eventPhase] + evt.currentTarget.toString().slice(8,-1));
}

function clearOutput(evt) {
    evt.stopPropagation();
    logElement.innerHTML = '';
}

const divs = document.getElementsByTagName('div');
for (const div of divs) {
  div.addEventListener('click', phase, true);
  div.addEventListener('click', phase, false);
}

document.addEventListener('click', gphase, true);
document.addEventListener('click', gphase, false);
window.addEventListener('click', gphase, true);
window.addEventListener('click', gphase, false);

const clearButton = document.getElementById('clear');
clearButton.addEventListener('click', clearOutput);

委託機制複習

//HTML
<div id="container">
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>

  <div class="tile"></div>
</div>

//CSS
.tile {
  height: 100px;
  width: 25%;
  float: left;
}

//JS
function random(number) {
  return Math.floor(Math.random()*number);
}

function bgChange() {
  const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
  return rndCol;
}

const container = document.querySelector('#container');

container.addEventListener('click', (event) => event.target.style.backgroundColor = bgChange());

參考資料

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events
https://developer.mozilla.org/zh-TW/docs/Web/API/EventTarget/dispatchEvent
https://css-tricks.com/lets-create-a-lightweight-native-event-bus-in-javascript/


上一篇
從事件中倖存(上) Day25
下一篇
操控 DOM(上) Day27
系列文
JS 忍者訓練計畫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言