封装事件处理


ECMAScript 本质部份〉这一系列文件,之所以要作为实验性质的文件,主要是想试试,如果有机会抛弃一些包袱的话,在浏览器上写 JavaScript 等,应该要做什么样的思考与设计,而在这一系列中,抛弃的最大包袱就是 Internet Explorer,如果你想要考量 Internet Explorer 兼容性,可以参考〈JavaScript 本质部份〉。

在事件这边,由于抛弃了 Internet Explorer 事件模型,相对于〈JavaScript 本质部份〉中对应的文件,你会发现事件封装时简单多了,可以在一个独立的 Evt-1.0.0.js 中编写事件模块:

// 主要在允许事件处理器以返回 false 的方式停止默认行为
function addEvtOn(elem, evtType, handler, capture = false) {
    elem.addEventListener(evtType, evt => {
        let result = handler.call(evt.currentTarget, evt);
        if(result === false) {
            evt.preventDefault();
        }
        return result;
    }, capture);
}

function removeEvtOn(elem, evtType, handler, capture = false) {
    elem.removeEventListener(evtType, handler, capture);
}

export {addEvtOn, removeEvtOn};

capture默认为false是合理的,在过去为了兼容于 Internet Explorer 不支持事件捕捉阶段,许多程序库与应用都未曾考量过捕捉阶段的作法,当然,如果不考量 Internet Explorer,也可以试着思考捕捉阶段可能的应用场合。

原本的 XD-1.0.0.js 现在变成了 XD-1.1.0.js,主要修改为导入了 Evt-1.0.0.js:

import {addEvtOn, removeEvtOn} from './Evt-1.0.0.js';

然后在ElemCollection类上,新增了addEvtremoveEvt两个方法:

class ElemCollection {

    ...

    // 新增事件处理
    addEvt(type, handler, capture = false) {
        this.elems.forEach(elem => addEvtOn(elem, type, handler, capture));
        return this;
    }

    // 移除事件处理
    removeEvt(type, handler, capture = false) {
        this.elems.forEach(elem => removeEvtOn(elem, type, handler, capture));
        return this;
    }
}

其余代码不变。来看看〈封装 DOM 操作〉中动态新增图片的范例,现在可以改写成底下(你的浏览器必须支持 ES6 模块):

<!DOCTYPE html>
<html>
<head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
</head>
<body>

    <input id="src" type="text"><button id="add">新增图片</button>
    <div id="images"></div>

<script type="module">
    import x from './js/XD-1.1.0.js';

    let doc = x(document);

    doc.elemsById('add').addEvt('click', evt => {

        let img = x('img').toElemCollection()
                          .attr('src', doc.elemsById('src').val())
                          .addEvt('click', evt => {
                              let target = evt.target;
                              target.parentNode.removeChild(target);
                          });      

        doc.elemsById('images').append(img);   

    });
</script>

</body>
</html>

按我观看结果

至于接下来的范例,也可以改写为:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>  
    容器一:
    <div id="container1">
        <img id="image" src="https://openhome.cc/Gossip/images/caterpillar_small.jpg"/>
    </div><br>
    容器二:
    <div id="container2"></div>  

<script type="module">
    import {elemsById} from './js/XD-1.1.0.js';

    let image = elemsById('image');

    image.addEvt('click', evt => {
        let c1 = elemsById('container1');
        let c2 = elemsById('container2');
        if(evt.target.parentNode === c1.get()) {
            c2.append(image);
        } else {
            c1.append(image);
        }
    });

</script>  
</body>
</html>

按我观看结果

可以在XD-1.1.0.jsEvt-1.0.0.js分别下载完整的 .js 文件。


展开阅读全文