8.18日更新
后文有一个寻找父节点的,我用的path数组,其实后来我再打印e看了一下,在target里面可以直接找到parentNode
,用这个的话更方便一些。
需求描述
项目是基于Vue的,想要实现的功能是:
1、点击ul就知道具体是哪一个li在点击
2、不给li绑定点击事件,只给ul绑定点击事件,因为li很多,一个个给li绑定事件比较麻烦
3、通过知道是点击的哪一个li,进而进行跳转
项目特殊
这个项目中,li标签下面还嵌套了两个标签,分别是img标签和span标签。后面会说到li
嵌套和不嵌套对解决这个问题的影响。
<li><img src="..." alt="" /><span>message</span></li>
关键属性
想要解决这些问题,就需要一些关键的属性,这些属性都是点击事件传过来的e中的
1、e.target
,这个属性的作用是获取当前点击的具体元素。
2、childElementCount
,这个属性的完整获取路径是e.target.childElementCount
,这个属性的值是该节点包含的子节点的数量。
3、path
,这个属性的值是一个数组,记录了从window
到这个节点所经过的所有节点。
4、dataset
,这个属性的值是一个数组,存储我们自己在标签中定义的属性以及对应的值。
问题分析&解决
1、首先肯定是给ul
绑定点击事件:
<ul @click="menuNavigation></ul>
2、给ul下面的每一个li都设置一个自定义属性,比如name,并设置一个值:
<li data-name="/home"><li><li data-name="/writing"><li>
3、现在我们点击某一个li,ul的click函数就会触发。那么怎么知道我们点击的是哪一个li呢?我们在第2步给每一个li都设置了一个自定义的属性name并赋了值,现在我们使用e.target.dataset
可以获取到当前点击的li的name的值,就相当于知道点击的是哪一个li了。
menuNavigation(e) {console.log(e.target.dataset.name) //"home" 或 "writing"}
4、现在有一个问题,如果li里面没有嵌套,直接就是第3步就行了。但是如果li里面嵌套了内容呢?比如本项目中嵌套了一个img和一个span,那么我们第3步还那么简单吗?答案是否定的。在控制台打印e.target
,可以发现如果我们点击的区域不同,获取到的e.target
的内容也不同。
menuNavigation(e) {console.log(e.target) //span 或者 img 或者 li}
于是当我们点击的地方不是li的时候,就不能直接通过e.target.dataset
获取到给li设置的name值了。那么,和li一样,给每一个li里面的img和span标签都设置一个自定义的属性值可以吗?可以,但是做起来也比较麻烦。
5、为了解决第4步中的问题,我们就要用到上文关键属性中的第二个。根据第3步的分析,只需要获取到当前点击的li,根据当前的li的name属性,就知道点击的是哪一个li了。以本项目为例,我的每一个li都是固定的,下面只有两个根节点,那么我通过判断点击的元素是否有子节点就能判断出点击的是li还是img或span。如果childElementCount
的值不为0,说明当前点击的元素含有子节点,说明我们点击的肯定就是li,如果为0,说明我们点击的就是span或者img。值不为0就很简单,就是第3步的情况。但是当值为0的时候呢?
不同的区域:
如上图,点击红色框线内的区域,e.target
获取到的就是img节点;点击绿色框线内区域,e.target获取到的就是span节点,点击黄色框线内,红色和绿色框线外的区域,获取到的就是li节点。每一个区域下的childElementCount
打印情况如下:
当点击的是li区域:
当点击的是span或者img区域:
6、当值为0的时候,我们就要用到上文关键属性中的第三个。既然我们取到了img或者span,那么我们就可以通过path
数组取到这个img或者span对应的父节点,也就是li,去到了li,就是第3步的情况了。
观察上图,结合本项目,只需要获取到数组中的第二个元素就能获取到li了,即
console.log(e.path[1]) // Node li
7、通过this.$router.path
可以获取到当前页面的路由,再通过以上步骤获取到的自定义属性name的值,做一个判断和跳转就ok了。
if (this.$route.path == toPath) returnthis.$router.push(toPath)
其中第一行的代码是防止用户重复跳转到某个相同的地址,比如点击了第一个菜单又点击第一个菜单,此时虽然不会出错,但是控制台会打印错误信息。加上这一行代码,可以防止这个情况。
最终代码
1、html部分:
<ul @click="menuNavigation"><li data-path="/home" class="type-activated"><img src="../assets/imgs/normal/blog.png" alt=""><span>学习博客</span></li><li data-path="/writing"><img src="../assets/imgs/normal/writing.png" alt=""><span>随笔</span></li></li>
2、js部分:
menuNavigation(e) {let toPath = ""if (e.target.childElementCount != 0) {toPath = e.target.dataset.path} else {toPath = e.path[1].dataset.path}if (this.$route.path == toPath) returnthis.$router.push(toPath)}