详解js的事件代理(委托)
发布时间 - 2026-01-10 22:00:45 点击率:次JavaScript事件代理(委托)一般用于以下情况:

1. 事件注册在祖先级元素上,代理其子级元素。可以减少事件注册数量,节约内存开销,提高性能。
2. 对js动态添加的子元素可自动绑定事件。
之前一直用各种js库的事件代理,如 jQuery,非常方便实用。今天尝试用原生 js 实现该功能。
var addEvent = (function () {
if (document.addEventListener) {
return function (element, type, handler) {
element.addEventListener(type, handler, false);
};
} else if (document.attachEvent) {
return function (element, type, handler) {
element.attachEvent('on' + type, function () {
handler.apply(element, arguments);
});
};
} else {
return function (element, type, handler) {
element['on' + type] = function () {
return handler.apply(element, arguments);
};
};
}
})(),
getClassElements = function (parentElement, classname) {
var all, element, classArr = [], classElements = [];
if (parentElement.getElementsByClassName) {
return parentElement.getElementsByClassName(classname);
} else {
all = parentElement.getElementsByTagName('*');
for (var i = 0, len = all.length; i < len; i++) {
element = all[i];
classArr = element && element.className && element.className.split(' ');
if (classArr) {
for (var j = 0; j < classArr.length; j++) {
if (classArr[j] === classname) {
classElements.push(element);
}
}
}
}
return classElements;
}
},
delegate = function () { // 参数:element, type, [selector,] handler
var args = arguments,
element = args[0],
type = args[1],
handler;
if (args.length === 3) {
handler = args[2];
return addEvent(element, type, handler);
}
if (args.length === 4) {
selector = args[2];
handler = args[3];
return addEvent(element, type, function (event) {
var event = event || window.event,
target = event.target || event.srcElement,
quickExpr = /^(?:[a-zA-Z]*#([\w-]+)|(\w+)|[a-zA-Z]*\.([\w-]+))$/,
match,
idElement,
elements,
tagName,
count = 0,
len;
if (typeof selector === 'string') {
match = quickExpr.exec(selector);
if (match) {
// #ID selector
if (match[1]) {
idElement = document.getElementById(match[1]);
tagName = match[0].slice(0, match[0].indexOf('#'));
// tag selector
} else if (match[2]) {
elements = element.getElementsByTagName(selector);
// .class selector
} else if (match[3]) {
elements = getClassElements(element, match[3]);
tagName = match[0].slice(0, match[0].indexOf('.'));
}
}
if (idElement) {
if ( tagName ? tagName === idElement.nodeName.toLowerCase() && target === idElement : target === idElement ) {
return handler.apply(idElement, arguments);
}
} else if (elements) {
for (len = elements.length; count < len; count++) {
if ( tagName ? tagName === elements[count].nodeName.toLowerCase() && target === elements[count] : target === elements[count] ) {
return handler.apply(elements[count], arguments);
}
}
}
}
});
}
};
主要是用 apply 改变 this 的指向
handler.apply(idElement, arguments); handler.apply(elements[count], arguments);
测试一下:
<style>
#outer {padding: 50px; background-color: lightpink;}
#inner {padding: 30px; background-color: aliceblue;}
#paragraph1, #paragraph3 {background-color: cadetblue}
</style>
<div id="outer">outer <div id="inner">inner <p id="paragraph1" class="parag1">paragraph1</p> <p id="paragraph2" class="parag">paragraph2</p> <span>span</span> <p id="paragraph3" class="parag">paragraph3</p> </div> </div>
var outer = document.getElementById('outer');
delegate(outer, 'click', function () {
console.log(this.id); // outer
});
delegate(outer, 'click', 'p', function () {
console.log(this.id); //点击 paragraph1 元素,输出其id为 "paragraph1"
});
模仿 jQuery 的风格,优化代码:
(function () {
var $ = function (element) {
return new _$(element);
};
var _$ = function (element) {
this.element = element && element.nodeType === 1 ? element : document;
};
_$.prototype = {
constructor: _$,
addEvent: function (type, handler, useCapture) {
var element = this.element;
if (document.addEventListener) {
element.addEventListener(type, handler, (useCapture ? useCapture : false));
} else if (document.attachEvent) {
element.attachEvent('on' + type, function () {
handler.apply(element, arguments);
});
} else {
element['on' + type] = function () {
return handler.apply(element, arguments);
};
}
return this;
},
getClassElements: function (classname) {
var element = this.element, all, ele, classArr = [], classElements = [];
if (element.getElementsByClassName) {
return element.getElementsByClassName(classname);
} else {
all = element.getElementsByTagName('*');
for (var i = 0, len = all.length; i < len; i++) {
ele = all[i];
classArr = ele && ele.className && ele.className.split(' ');
if (classArr) {
for (var j = 0; j < classArr.length; j++) {
if (classArr[j] === classname) {
classElements.push(ele);
}
}
}
}
return classElements;
}
},
delegate: function () { //参数:type, [selector,] handler
var self = this,
element = this.element,
type = arguments[0],
handler;
if (arguments.length === 2) {
handler = arguments[1];
return self.addEvent(type, handler);
} else if (arguments.length === 3) {
selector = arguments[1];
handler = arguments[2];
return self.addEvent(type, function (event) {
var event = event || window.event,
target = event.target || event.srcElement,
quickExpr = /^(?:[a-zA-Z]*#([\w-]+)|(\w+)|[a-zA-Z]*\.([\w-]+))$/,
match,
idElement,
elements,
tagName,
count = 0,
len;
if (typeof selector === 'string') {
match = quickExpr.exec(selector);
if (match) {
// #ID selector
if (match[1]) {
idElement = document.getElementById(match[1]);
tagName = match[0].slice(0, match[0].indexOf('#'));
// tag selector
} else if (match[2]) {
elements = element.getElementsByTagName(selector);
// .class selector
} else if (match[3]) {
elements = self.getClassElements(match[3]);
tagName = match[0].slice(0, match[0].indexOf('.'));
}
}
if (idElement) {
if ( tagName ? tagName === idElement.nodeName.toLowerCase() && target === idElement ? target === idElement ) {
return handler.apply(idElement, arguments);
}
} else if (elements) {
for (len = elements.length; count < len; count++) {
if ( tagName ? tagName === elements[count].nodeName.toLowerCase() && target === elements[count] : target === elements[count] ) {
return handler.apply(elements[count], arguments);
}
}
}
}
});
}
}
};
window.$ = $;
return $;
}());
使用如下:
var outer = document.getElementById('outer');
$(outer).delegate('click', '.parag', function (event) {
console.log(this.id);
});
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
# js
# 事件代理
# js事件委托和事件代理案例分享
# js中的事件委托或是事件代理使用详解
# JavaScript事件代理和委托详解
# 深入分析Javascript事件代理
# 浅析javascript中的事件代理
# JavaScript通过事件代理高亮显示表格行的方法
# JavaScript的事件代理和委托实例分析
# javascript中的事件代理初探
# JavaScript 事件代理需要注意的地方
# 绑定
# 可以减少
# 其子
# 主要是
# 测试一下
# getElementsByTagName
# getElementsByClassName
# length
# len
# classElements
# classname
# parentElement
# getClassElements
# classArr
# lt
# event
# args
# window
# srcElement
# target
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
javascript日期怎么处理_如何格式化输出
怎么用AI帮你设计一套个性化的手机App图标?
用yum安装MySQLdb模块的步骤方法
新三国志曹操传主线渭水交兵攻略
Laravel如何发送系统通知_Laravel Notifications实现多渠道消息通知
Laravel如何实现API速率限制?(Rate Limiting教程)
Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程
Python自然语言搜索引擎项目教程_倒排索引查询优化案例
Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱动更新修复【详解】
西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?
ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
如何构建满足综合性能需求的优质建站方案?
如何选择PHP开源工具快速搭建网站?
在线制作视频网站免费,都有哪些好的动漫网站?
网站制作大概要多少钱一个,做一个平台网站大概多少钱?
网站制作报价单模板图片,小松挖机官方网站报价?
制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?
Laravel怎么实现支付功能_Laravel集成支付宝微信支付
香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南
为什么要用作用域操作符_php中访问类常量与静态属性的优势【解答】
Laravel如何将应用部署到生产服务器_Laravel生产环境部署流程
laravel怎么为应用开启和关闭维护模式_laravel应用维护模式开启与关闭方法
如何生成腾讯云建站专用兑换码?
香港服务器部署网站为何提示未备案?
长沙企业网站制作哪家好,长沙水业集团官方网站?
php后缀怎么变mp4格式错误_修改扩展名提示格式不对怎么办【技巧】
如何在阿里云服务器自主搭建网站?
网站制作大概多少钱一个,做一个平台网站大概多少钱?
Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析
Laravel如何保护应用免受CSRF攻击?(原理和示例)
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复
教学论文网站制作软件有哪些,写论文用什么软件
?
如何有效防御Web建站篡改攻击?
Android中AutoCompleteTextView自动提示
如何快速查询网址的建站时间与历史轨迹?
如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南
详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)
nginx修改上传文件大小限制的方法
非常酷的网站设计制作软件,酷培ai教育官方网站?
潮流网站制作头像软件下载,适合母子的网名有哪些?
js实现点击每个li节点,都弹出其文本值及修改
如何撰写建站申请书?关键要点有哪些?
如何在阿里云虚拟服务器快速搭建网站?
宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法
Laravel如何实现全文搜索功能?(Scout和Algolia示例)
java ZXing生成二维码及条码实例分享
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
如何在云服务器上快速搭建个人网站?

