最近在研究react的事件,都说他是合成事件,整个dom唯一绑定事件的是document。
我想这不就是事件委托么,有啥好研究的,于是我去重温了下事件委托,顺便看了阻止冒泡。
突然突发奇想,如果两个结合在一起呢?
能否实现子节点点击了,父节点不能触发点击事件
好像n年前我还是小萌新,在广电商做切图仔的时候请教过我的组长,他没有给到我满意的答案。今天这个想法又冒出来了。
我得解决它一下。
它用了 e.nativeEvent.stopImmediatePropagation();
nativeEvent.stopImmediatePropagation是react自己新增的属性方法。
那我的想法就来了。我也可以啊。
- <div class="a">
- <div class="b">
- <div class="e">点击<b>捣蛋鬼E</b>,会执行
- <B>e,b,a</B>函数
- </div>
- <div class="c">点击<b>捣蛋鬼C,stop</b>,执行执行<b>c</b>函数</div>
- <div class="f">点击<b>捣蛋鬼F</b>,会执行<b>f,b,a</b>函数</div>
- </div>
- </div>
- function ProxyClickEvent(proxyBox) {
- var self = this;
- self.eventPool = [];
- if (proxyBox) {
- self._proxyBox = proxyBox;
- } else {
- self._proxyBox = document.body;
- }
-
- self.__bindEvent = self._bindEvent.bind(self);
- self._proxyBox.addEventListener("click", self.__bindEvent)
- }
- ProxyClickEvent.prototype._bindEvent = function(e) {
- let self = this;
- let target = e.target;
- for (let i = self.eventPool.length - 1; i >= 0; i--) {
- let event = self.eventPool[i];
- let fn = event.fn;
-
- /**
- * 1.如果target等于事件对应的dom
- * 2.否则事件dom是target的父节点&&e._target不为false
- * 3.如果e._stop为true时候,break
- * 那就执行回调函数
- *
- * **/
- if (event.dom == e.target) {
- fn(e);
- } else if (!e.__stop && event.dom.contains(e.target)) {
- fn(e);
- } else if (e.__stop) {
- break;
- }
- }
- }
- ProxyClickEvent.prototype.add = function(dom, fn) {
- if (!(dom instanceof HTMLElement)) {
- console.error("invalid parameter dom", dom, "fn", fn)
- throw "function add arg1 should be a HTMLElement,but get " + (typeof dom)
- }
- if (typeof fn !== "function") {
- console.error("invalid parameter dom", dom, "fn", fn)
- throw "function add arg2 should be a function,but get " + (typeof fn)
- }
- this.eventPool.push({
- dom: dom,
- fn: fn
- })
- }
- ProxyClickEvent.prototype.remove = function(dom, fn) {
- this.eventPool = this.eventPool.filter(item => item.dom != dom && item.fn != fn)
- }
- ProxyClickEvent.prototype.destroyed = function() {
- this.eventPool = [];
- self._proxyBox.removeEventListener("click", this.__bindEvent);
- }
- let proxyEvent = new ProxyClickEvent();
- window.onload = function() {
- proxyEvent.add(document.querySelector(".a"), function() {
- console.log("点击了a")
- })
- proxyEvent.add(document.querySelector(".b"), function() {
- console.log("点击了b")
- })
- proxyEvent.add(document.querySelector(".c"), function(e) {
- e.__stop = true;
- console.log("点击了c")
- })
- proxyEvent.add(document.querySelector(".e"), function() {
- console.log("点击了e")
- })
- proxyEvent.add(document.querySelector(".f"), function() {
- console.log("点击了f")
- })
- }
必须得从父级到子节点,按顺序add,否则事件“冒泡顺序”会错乱
目前先假设使用场景场景:
用户可以自己明确且可以控制从父级到自己按顺序的add
通过dom自定义属性clickFn来绑定函数名字,然后获取携带clickFn属性的dom,遍历后追加点击事件
- <!DOCTYPE html>
- <html lang="en">
-
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- </head>
-
- <body>
- <div class="a" clickFn="fna">
- <div class="b" clickFn="fnb">
- <div class="e" clickFn="fne">点击<b>捣蛋鬼E</b>,会执行
- <B>e,b,a</B>函数
- </div>
- <div class="c" clickFn="fnc">点击<b>捣蛋鬼C,stop</b>,执行执行<b>c</b>函数</div>
- <div class="f" clickFn="fnf">点击<b>捣蛋鬼F</b>,会执行<b>f,b,a</b>函数</div>
- </div>
- </div>
- <script src="./ProxyClickEvent.js"></script>
- <script>
-
- var funs = {
- fna: function() {
- console.log("点击a")
- },
- fnb: function() {
- console.log("点击b")
- },
- fnc: function(e) {
- e.__stop = true;
- console.log("点击c")
- },
- fnd: function() {
- console.log("点击d")
- },
- fne: function() {
- console.log("点击e")
- },
- fnf: function() {
- console.log("点击f")
- }
- }
- window.onload = function() {
- let proxyEvent = new ProxyClickEvent();
- let clickDoms = document.querySelectorAll("[clickFn]");
- for (let i = 0; i < clickDoms.length; i++) {
- let dom = clickDoms[i];
- let fnStr = clickDoms[i].getAttribute("clickFn");
- proxyEvent.add(dom, funs[fnStr])
- }
- }
- </script>
- </body>
-
- </html>
某个元素删除后,点击时候回调函数依旧会触发
建议:如果涉及到dom的父子节点的增删的话,不使用这套代码,因为add的次序被破坏了,会影响“冒泡顺序”
- function ProxyClickEvent(proxyBox) {
- var self = this;
- self.eventPool = [];
- if (proxyBox) {
- self._proxyBox = proxyBox;
- } else {
- self._proxyBox = document.body;
- }
- //更新数组,过滤掉被销毁的dom的事件
- self._bind_clearRemoveDomEventItem = self._clearRemoveDomEventItem.bind(self);
- //定时过滤dom不存在的元素
- self._timer = setInterval(function() {
- self._bind_clearRemoveDomEventItem();
- }, 1000)
- self.__bindEvent = self._bindEvent.bind(self);
- self._proxyBox.addEventListener("click", self.__bindEvent)
- }
- ProxyClickEvent.prototype._clearRemoveDomEventItem = function() {
- if (this.eventPool.some((el) => !document.body.contains(el.dom))) {
- this.eventPool = this.eventPool.filter(item => document.body.contains(item.dom));
- }
- }
- ProxyClickEvent.prototype._bindEvent = function(e) {
- let self = this;
- let target = e.target;
- for (let i = self.eventPool.length - 1; i >= 0; i--) {
- let event = self.eventPool[i];
- let fn = event.fn;
-
- /**
- * 1.如果target等于事件对应的dom
- * 2.否则事件dom是target的父节点&&e._target不为false
- * 3.如果e._stop为true时候,break
- * 那就执行回调函数
- *
- * **/
- if (event.dom == target) {
- fn(e);
- } else if (!e.__stop && event.dom.contains(target)) {
- fn(e);
- } else if (e.__stop) {
- break;
- }
- }
- }
- ProxyClickEvent.prototype.add = function(dom, fn) {
- if (!(dom instanceof HTMLElement)) {
- console.error("invalid parameter dom", dom, "fn", fn)
- throw "function add arg1 should be a HTMLElement,but get " + (typeof dom)
- }
- if (typeof fn !== "function") {
- console.error("invalid parameter dom", dom, "fn", fn)
- throw "function add arg2 should be a function,but get " + (typeof fn)
- }
-
- this.eventPool.push({
- dom: dom,
- fn: fn
- })
- }
- ProxyClickEvent.prototype.remove = function(dom, fn) {
- this.eventPool = this.eventPool.filter(item => item.dom != dom && item.fn != fn)
- }
- ProxyClickEvent.prototype.destroy = function() {
- this.eventPool = [];
- this._proxyBox.removeEventListener("click", this.__bindEvent);
- clearInterval(this._timer);
- }