【问题标题】:Emulate jQuery "on" with selector in pure Javascript在纯 Javascript 中使用选择器模拟 jQuery “on”
【发布时间】:2021-06-09 19:39:26
【问题描述】:

我会在纯 Javascript 中模拟 jQuery .on( events , selector , data) 方法的主要功能。

例如

$(document).on('click','.button',function() {
   console.log("jquery onclick"); 
});

我认为制作这样的东西就足够了

document.addEventListener('click',function(e) {
    if(e.target.className == 'button2') {
         console.log("It works");   
    }
});

但是当我有这个 html 结构时:

<button class="button2">Hello <span>World</span></button>

当在span 元素上触发点击事件时,我的脚本不起作用,因为e.targetspan。 (对于这个问题,我忽略了具有多个类的元素的复杂性以及跨浏览器兼容性)

source of jQuery 读起来并不简单,我不明白它是如何工作的(因为第一段代码,在 jQuery 中,适用于我的 html 结构)。

我需要这个方法,因为我的 html 是动态的,并且这个类的按钮被创建、删除和重新创建了很多次。我不想每次都添加监听器。

如果可能,我会避免包含 jquery 库。

那么,我能做到吗?

这里是jsFiddle 用于测试。

【问题讨论】:

  • 也许 jQuery 代码如此复杂是有原因的……
  • @Asad 很有趣...但这不会因为 DOM 太深而太重?
  • @chumkiu Kolink 已经发布了一个这样的例子。这将是资源密集型的,但如果您附加到靠近元素的祖先,应该不会太糟糕。
  • @Asad 但如果我点击任何其他元素,这会为每个元素创建一个正则表达式,直到正文。

标签: javascript dom-events


【解决方案1】:

2017 年更新current DOM standards like closest 表示现在更容易了。

const addEventForChild = function(parent, eventName, childSelector, cb){      
  parent.addEventListener(eventName, function(event){
    const clickedElement = event.target,
    matchingChild = clickedElement.closest(childSelector)
    if (matchingChild) cb(matchingChild)
  })
};

仅使用它:

addEventForChild(parent, 'click', '.child', function(childElement){
  console.log('Woo click!', childElement)
})

这是jsfiddle

【讨论】:

  • 这看起来不错 - 但它不支持 IE11 或 IE10? caniuse.com/#feat=element-closest
  • @AndrewNewby 不知道你为什么在 2018 年完全支持 IE,但你可以填充 element.closest 并使用它直到你放弃 IE。
  • 不幸的是,5% 的用户仍在使用 IE11,8% 的用户使用 Edge。烦死了!我只是在玩它。出于某种原因,它很奇怪(当我不期待它时触发)。希望我能把它弄好,因为这是我在周围找到的所有其他东西的更好(和更简单)的代码:)
  • @AndrewNewby 不用担心,祝你好运! Edge 很酷(有时领先于 Chrome)顺便说一句。
  • 如果委托元素本身是与选择器匹配的元素的后代,这将失败。正确地说,它应该在委托元素处停止(并忽略这个委托元素)。这是一个显示问题的小提琴:jsfiddle.net/hm3skn9p,这是修复:jsfiddle.net/eu6zn0pw
【解决方案2】:

这实际上非常简单。你在正确的轨道上,但它并不完全在那里。

这是我使用的函数:

window.addEvent = function(elem,type,callback) {
    var evt = function(e) {
        e = e || window.event;
        return callback.call(elem,e);
    }, cb = function(e) {return evt(e);};
    if( elem.addEventListener) {
        elem.addEventListener(type,cb,false);
    }
    else if( elem.attachEvent) {
        elem.attachEvent("on"+type,cb);
    }
    return elem;
};
window.findParent = function(child,filter,root) {
    do {
        if( filter(child)) return child;
        if( root && child == root) return false;
    } while(child = child.parentNode);
    return false;
};
window.hasClass = function(elem,cls) {
    if( !('className' in elem)) return;
    return !!elem.className.match(new RegExp("\\b"+cls+"\\b"));
};

window.findParent 是整个事情的核心,正如我向您展示如何附加您想要的 on 监听器时您所看到的那样:

window.addEvent(document.body,"click",function(e) {
    var s = window.findParent(e.srcElement || e.target,function(elm) {
        return window.hasClass(elm,"button");
    },this);
    if( s) {
        console.log("It works!");
    }
});

【讨论】:

  • 没关系。但是在 findParent 函数的while 中,您不应该检查 child != original elem? (在本例中为 document.body)。因为如果它是一个简单的 div,你不应该在 document.body 之前检查所有父级)
  • 确实如此。我从来没有真正想过这一点,因为我总是使用身体作为单一的“不变”元素。我将更新此处发布的代码以允许变量 root。
  • 我还有一个错字(缺少右括号)。解决了这个问题。
  • 好的,谢谢。 +1。你确定这是唯一的方法吗?因为,恕我直言,对于与原始过滤器不匹配的文档正文中的每次单击(单击另一个按钮,或简单的锚点 ecc),它都很沉重。对于每次单击,此脚本搜索每个元素,直到主标记(正文),并为所有这些在 className 上创建一个正则表达式。 jquery 也这样做吗?
  • 我不知道 jQuery 是如何工作的,但我无法想象它会有很大的不同(只是效率要低得多)
【解决方案3】:

简单而简短的代码:

function onEvt(type, callback) {
    if (document.attachEvent) {
        document.attachEvent("on" + type, function (e) {
            callback(e.target);
        });
    } else {
        document.addEventListener(type, function (e) {
            callback(e.target);
        }, false);
    }
}

这样调用函数:

onEvt('click', function(elem){ // click, mouseover etc...
    // for class
    if(elem.classList.contains('classname')){
        // do stuff
    }
    // for attribute
    if(elem.hasAttribute('target')){
        // do stuff
    }
});

【讨论】:

  • 如果单击的元素有子元素,这将不起作用 - 例如,您单击 '.option' 但该元素实际上是 .option 元素中的 span
  • 感谢您的反馈。请您在jsfiddle 上给我一个实时代码吗?
  • 当然是克里希纳!见jsfiddle.net/34dfgnkx。比较点击蓝色区域与点击白色区域。
  • 没什么期待的,我已经添加了 jsfiddle!请参阅上面评论中的链接。
【解决方案4】:

您可以通过link 使用 github 上的函数 Marchy8 或通过执行以下操作来扩展它。

首先创建$()选择器函数:

function $(selector) {
    return selector;
}

HTMLElement = typeof (HTMLElement) != "undefined" ? HTMLElement : Element;
String.prototype.on = function(type, callback){
    var selector = this;
    document.body.addEventListener(type, function (event) {
        if (event.target.matches(selector)) {
            callback.call(event.target);
        }
    });
}

$(".test").on("click", function(e) {
    console.log(this.innerHTML);
    this.style.color = "red";
});
<div class="test">Click Here</div>
<div class="test">Click Here-1</div>

【讨论】:

  • 不给String添加on函数能找到解决办法吗?也许通过从 $ 函数返回一个自定义对象?
猜你喜欢
  • 2013-12-15
  • 1970-01-01
  • 2016-06-04
  • 1970-01-01
  • 1970-01-01
  • 2017-02-12
  • 1970-01-01
  • 1970-01-01
  • 2011-04-12
相关资源
最近更新 更多