先说说我为什么有这种“奇怪”的想法。
它基于这样一个场景:我最近闲来无事完善了一个小demo:音乐播放器。在里面有一个功能 ——
点击列表某一项弹出音乐播放弹框。我原先一直是“为每一项单独加一个click事件监听”。这很糟糕!
<div id="videob"></div> //js代码 videob.onclick=function(){ let video=document.
createElement('video') video.setAttribute('src','xxx') video.setAttribute(
'controls','controls') video.style.cssText="width:90%;height:300px" document.
querySelector('body').appendChild(video) }
后来我优化时改用了 事件代理
,不得不承认它非常优秀,但是我不得不改变结构,使得js中出现了一堆“MP4数据”(一个数组),在触发事件时判断当前是哪一个,然后从数组中拿到赋值给audio 的
src 。

事实上,在开发 Web 应用时,经常会用 JavaScript 获取文档之外的信息 ,某些情况下,我们需要用一些技巧来处理这些额外信息以保证 Web
应用能够正常运行。一般而言,技巧无外乎是将额外的信息塞入事件处理程序或滥用 rel 属性或 class 属性以方便注入行为。

幸运的是,HTML5引入了自定义数据属性:所有的自定义数据属性都以 data-前缀开头,HTML5
文档的验证器会在验证时忽略它。开发人员可以在任意元素中加入自定义数据属性,属性值可以是照片的元数据、经纬度坐标或者弹出窗口的尺寸。
最棒的是,几乎在所有浏览器中,你都能够使用自定义数据属性,因为我们可以轻易地使用 JavaScript 来获取它们。

为什么设置onclick不好
在开始操作之前,我们必须先“重复”一个概念:0级DOM事件
。通俗来说就是各种on前缀的事件名称(比如:onclick、onmouseon、onkeydown),他们可以存在于<script>
或者HTML标签中,且没有事件捕获和冒泡机制!

有这样一个场景:Web 应用的开发人员尤其是初学者常常需要依赖于弹出窗口,以便向用户显示在线帮助信息、附加选项或者其他重要的用户界面功能。
我们经常能在一个页面中看到如下代码:
<a href="#" onclick="window.open('xxx',null,'width=300,height=300');">go to xxx
</a>
就是这段没怎么起眼的代码存在着非常致命的问题:

* 链接的目标地址没有设置!如果 JavaScript
被禁用了,那么链接将无法引导用户进入相应页面。这很一个非常严重,以至于我们需要立即解决。我建议开发者永远不要省略 href属性、任何情况下都不要为 href
属性赋“#”。
* 注意保持行为与内容分离,正如用链接样式表保持样式信息分离一样。开始的时候,使用 onclick 会带来便利,但是想象一下页面上有 50
个链接的情况,那时你会看到 onclick 方法失控的场面。你将只能一遍又一遍地编写重复的 JavaScript
代码。如果是通过服务器端代码来生成浏览器端代码,那么你就是在增加 JavaScript 事件。
为了解决上面的问题,我们为每个a链接设置了href,而且增加了不同的class!以增加“相同类型元素”的可识别性。
<a href="xxx" class="pop">go to xxx</a> //js代码 var link=document.querySelector(
'a.pop') link.onclick=function(e){ e.preventDefault(); window.open(this.
getAttribute('href')); }
我们还没有为open设置长宽!

现在,我们又回到了和文章开头一样的问题!

使用自定义数据属性
当创建应用了 JavaScript 的 Web 应用时,刚刚提到的情况比较常见。如你所见,在代码中存储窗口的期望高度和宽度是可取的,但是 onclick
方法有诸多弊端。我们可以改换在元素上嵌入属性的方式加以实现。现在要做的是将链接改造成下面这种形式:
<a href="xxx" data-width="600" data-height="400" class="pop">go to xxx</a> var
link=document.querySelector('a.pop') link.onclick=function(e){ e.preventDefault(
); window.open(this.getAttribute('href'),null,"width="+this.getAttribute(
'data-width')+",height="+this.getAttribute('data-height')); }
好吧,这个例子似乎没有什么说服力。
但是为了让你能够真正认识到“问题的严重性”,我将带你来优化文章开头的示例:
<div id="mxc"> <div class="videob" data-uri="./img/1.mp4"></div> <div class=
"videob" data-uri="./img/2.mp4"></div> </div> mxc.addEventListener('click',
function(e){ let target=e.target || e.srcElement; let video=document.
createElement('video') video.setAttribute('src',target.getAttribute('data-uri'))
video.setAttribute('controls','controls') video.style.cssText=
"width:90%;height:300px;margin:0 auto" document.querySelector('body').
appendChild(video) },false)
这样才“真正”体现了“代理的价值”!

2020-09-09更新
发现一个没怎么注意的事情:父元素事件中的 e.target
是直接指向了“当前触发事件的子元素的最底层子元素”,其上面的“父元素”都将以parentNode的形式被展示出来(原型上)。

发现这个是因为想要换一种思路写一个“类原生相册”,我的思路是:开始时总的宽度是100vw,子元素(相册图片)以flex排列;点击某一个图片预览时动态给父元素添加一个类名,这个类的作用是:将父元素下(单张)图片的最大宽度设为100vw(子元素从始至终都不设宽)。然后在js中根据子元素的长度计算其“真正宽度”(
.style.width)。并根据当前点击的是第几个子元素计算应该transform偏移多少距离?

其核心代码:
//计算当前点击元素是父元素中的第几个子元素 == jQuery:index() var child = document.getElementById(
xxx); var index = Array.prototype.indexOf.call(child.parentNode,child);
其他用到“自定义数据属性”最多的地方应该就是【图片懒加载】里面设置替换图了吧:
<body> <div class="imgs"> <div> <img src="占位图" data-src="真正图片" alt="图片说明"> </
div> ... </div> <script> window.onload=function () { var imgs=document.
querySelectorAll('img'); function offset(el){ let top=el.offsetTop while(el.
offsetParent){ el=el.offsetParent top+=el.offsetTop
//scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离 } return { top } } function lazyload(
imgs) { var h=window.innerHeight; //innerheight 返回窗口的文档显示区的高度 var s=document.
documentElement.scrollTop || document.body.scrollTop; //document.body.scrollTop
网页被卷去的高 for(var i=0;i<imgs.length;i++){ if((h+s)>offset(imgs[i]).top){
//窗口文档显示去高度+向上滚动的高度,判断是否到达了图片的位置,如果到了,才去加载,并在加载中 用一个动图去“占位” (function (i) {
setTimeout(function () { var tmp=new Image(); tmp.src=imgs[i].getAttribute(
"data-src"); // 直接获取 tmp.onload=function () { imgs[i].src=imgs[i].getAttribute(
"data-src"); // 如果直接获取不了(还没加载完),就等加载完前面的再去获取 } },1300) })(i) } } } lazyload(imgs
); window.onscroll=function () { lazyload(imgs); } } </script> </body>
事实上,我们确实在很多地方可以用到它:比如自定义组件(vue / 微信小程序)中我们可以为它设置参数为它本身携带的值。

展望
开发者可以用自定义数据属性将各式各样的信息嵌入标记中。
我们可以使用自定义数据属性将日期和时间缓存在页面中,并以用户时区为基准来显示日期和时间。只需将时间以 UTC 的形式置于 HTML
页面中,在客户端将其转换成用户本地时间即可。(主要配合浏览器的缓存:在页面信息未改变时不会更新)

技术
©2019-2020 Toolsou All rights reserved,
Java开发2020年最新常见面试题整理【Spring源码分析】42-@Conditional详解element-ui踩坑记录神仙面试宝典你有了吗?半月看完25大专题,居然斩获阿里P8offer使用css样式设计一个简单的html登陆界面XCTF攻防世界web新手练习_ 9_command_executionJS中的解构赋值的详解与具体用途Python Web 框架elementui 穿梭框 el-transfer 展示列表内容文字过长ConcurrentHashMap实现原理及源码解析