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