千家信息网

如何自定义类似于jQueryUISelectable的Vue指令v-selectable

发表于:2025-11-15 作者:千家信息网编辑
千家信息网最后更新 2025年11月15日,这期内容当中小编将会给大家带来有关如何自定义类似于jQueryUISelectable的Vue指令v-selectable,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收
千家信息网最后更新 2025年11月15日如何自定义类似于jQueryUISelectable的Vue指令v-selectable

这期内容当中小编将会给大家带来有关如何自定义类似于jQueryUISelectable的Vue指令v-selectable,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

话不多说,先看效果。

  其实就是一个可以按住鼠标进行一个区域内条目选择的功能,相信用过Jquery UI 的都知道这是selectable的功能,然而我们如果用Vue开发的话没有类似的插件,当然你仍然可以把jquery的拿过来直接用,但是我又不想引入jquery 和 jquery UI在我的项目中,于是我就自己尝试着实现类似的功能。

  要实现这个功能分两步。第一步是实现鼠标选择区域的功能,第步部是把这个区域内被选择的item添加一个active的类。

  先看如何实现按住鼠标画虚线框,思路是先把容器元素的定位改为relative 然后判断当鼠标按下(mousedown)的时候,进行记住这个点击点的位置(e.layerX , e.layerY),然后鼠标移动(mousemove)的时候,实时的监测鼠标的位置(e.layerX , e.layerY),有了这两个位置就可以动态的创建一个div,它的定位为absolute,然后把它添加的容器框里,并且每次清空前一个框就可以了。为什么是用e.layerX e.layerY呢,

layerX layerY

如果元素的position样式不是默认的static,我们说这个元素具有定位属性。

在当前触发鼠标事件的元素和它的祖先元素中找到最近的具有定位属性的元素,计算鼠标与其的偏移值,以找到元素的border的左上角的外交点作为相对点。如果找不到具有定位属性的元素,那么就相对于当前页面计算偏移,此时等同于pageY。按照这个思路完成以下代码:

export default (Vue, options = {}) =>{  const listener = (ele, binding) =>{    let reactArea = {      startX: 0,      startY: 0,      endX: 0,      endY: 0    }    //是否一直按下鼠标    let isMouseDown = false    let areaSelect = {}    //将元素定位改为relative    ele.style.position = 'relative'    ele.addEventListener('mousedown', function(e) {      reactArea.startX = e.layerX;      reactArea.startY = e.layerY;      isMouseDown = true    })    ele.addEventListener('mousemove', function(e) {      if(isMouseDown){        let preArea = ele.getElementsByClassName('v-selected-area')        if(preArea.length){          ele.removeChild(preArea[0])        }        reactArea.endX = e.layerX        reactArea.endY = e.layerY        let leftValue = 0        let topValue = 0        let widthValue = Math.abs(reactArea.startX - reactArea.endX)        let heightValue = Math.abs(reactArea.startY - reactArea.endY)        if(reactArea.startX >= reactArea.endX){          leftValue = reactArea.endX        }else{          leftValue = reactArea.startX        }        if(reactArea.startY > reactArea.endY ){          topValue = reactArea.endY        }else{          topValue = reactArea.startY        }        //判断同时有宽高才开始画虚线框        if(reactArea.startX != reactArea.endX && reactArea.startY !=reactArea.endY){          areaSelect = document.createElement('div')          areaSelect.classList.add("v-selected-area")          areaSelect.style.position = "absolute";          areaSelect.style.left = leftValue + 'px'          areaSelect.style.top = topValue + 'px'          areaSelect.style.width = widthValue + 'px'          areaSelect.style.height = heightValue + 'px'          areaSelect.style.border = "1px dashed grey"          ele.append(areaSelect)        }      }    })    ele.addEventListener('mouseup', function(e) {      isMouseDown = false      //每次鼠标点击完了areaSelect      if(areaSelect && areaSelect.childNodes && ele.contains(areaSelect)){        ele.removeChild(areaSelect)      }      areaSelect = null    })  }   Vue.directive('selectable',{    inserted:listener,    updated:listener  })}

  这个时就可以实现画虚线框的效果

  下一步是如何把每个item置为选中状态。思路是遍历这个容器ul 的所有子元素li ,然后判断每个li是否在选中的框内部。然后看每个元素的offsetLeft 和 offsetTop 计算元素相对于父元素的位置,然后通过getBoundingClientRect().height 和 getBoundingClientRect().width 确定子元素的宽高。这些就可以计算出元素的位置和大小了,然后如何判断这个元素是否在选择区域内呢?我的规则是这个元素的四个角位置有任何一个在选择区域内或者选择区域就在这个区域的内部,就算是这个元素被选中了(这个判断方式感觉不是很完美)。按照这个思路,继续完成我们的代码:

export default (Vue, options = {}) =>{ const listener = (ele, binding) =>{ let reactArea = {  startX: 0,  startY: 0,  endX: 0,  endY: 0 } //是否一直按下鼠标 let isMouseDown = false let areaSelect = {} //将元素定位改为relative ele.style.position = 'relative' ele.addEventListener('mousedown', function(e) {  reactArea.startX = e.layerX;  reactArea.startY = e.layerY;  isMouseDown = true }) ele.addEventListener('mousemove', function(e) {  if(isMouseDown){   let preArea = ele.getElementsByClassName('v-selected-area')  if(preArea.length){   ele.removeChild(preArea[0])  }  reactArea.endX = e.layerX  reactArea.endY = e.layerY  let leftValue = 0  let topValue = 0  let widthValue = Math.abs(reactArea.startX - reactArea.endX)  let heightValue = Math.abs(reactArea.startY - reactArea.endY)  if(reactArea.startX >= reactArea.endX){   leftValue = reactArea.endX  }else{   leftValue = reactArea.startX  }  if(reactArea.startY > reactArea.endY ){   topValue = reactArea.endY  }else{   topValue = reactArea.startY  }  //判断同时有宽高才开始画虚线框  if(reactArea.startX != reactArea.endX && reactArea.startY !=reactArea.endY){   areaSelect = document.createElement('div')   areaSelect.classList.add("v-selected-area")   areaSelect.style.position = "absolute";   areaSelect.style.left = leftValue + 'px'   areaSelect.style.top = topValue + 'px'   areaSelect.style.width = widthValue + 'px'   areaSelect.style.height = heightValue + 'px'   areaSelect.style.border = "1px dashed grey"   ele.append(areaSelect)  }  let children = ele.getElementsByTagName('li')  for(let i =0 ; i < children.length ; i ++ ){   let childrenHeight = children[i].getBoundingClientRect().height   let childrenWidth = children[i].getBoundingClientRect().width   //每个li元素的位置   let offsetLeft = children[i].offsetLeft   let offsetTop = children[i].offsetTop   //每个li元素的宽高   let endPositionH = childrenHeight + offsetTop   let endPositionW = childrenWidth + offsetLeft   //五个条件满足一个就可以判断被选择   //一是右下角在选择区域内   let require1 = endPositionH > topValue && endPositionW > leftValue && endPositionH < topValue + heightValue && endPositionW < leftValue + widthValue   //二是左上角在选择区域内   let require2 = offsetTop > topValue && offsetLeft > leftValue && offsetTop < topValue + heightValue && offsetLeft < leftValue + widthValue   //三是右上角在选择区域内   let require3 = offsetTop > topValue && offsetLeft + childrenWidth > leftValue && offsetTop < topValue + heightValue && offsetLeft + childrenWidth< leftValue + widthValue   //四是左下角在选择区域内   let require4 = offsetTop + childrenHeight > topValue && offsetLeft > leftValue && offsetTop + childrenHeight < topValue + heightValue && offsetLeft < leftValue + widthValue   //五选择区域在元素体内   let require5 = offsetTop < topValue && offsetLeft < leftValue && offsetTop + childrenHeight > topValue + heightValue && offsetLeft + childrenWidth > leftValue + widthValue   if(require1 || require2 || require3 || require4 || require5){   children[i].classList.add('active')   }else{   children[i].classList.remove('active')   }  }  } }) ele.addEventListener('mouseup', function(e) {  isMouseDown = false  if(areaSelect && areaSelect.childNodes && ele.contains(areaSelect)){  ele.removeChild(areaSelect)  }  areaSelect = null }) } Vue.directive('selectable',{ inserted:listener, updated:listener })}

完成之后再看看如何使用,html 结构:

      
  • item1  
  •   
  • item2  
  •   
  • item3  
  •   
  • item4  
  •   
  • item5  
  •   
  • item6  

  注意ul的这个v-selectable就是我们自定义的指令,但是使用之前必须 Vue.use

import Vue from 'vue'import Selectable from '@/components/vue-selectable/vue-selectable.js' //这个修改为你的js路径Vue.use(Selectable);

  再给我们的ul li 加点样式,注意我们的被选择项会被添加一个active的class,通过这个来改变选中项样式

  这样就可以达到开头的效果了。实际上代码运行过程中还是有许多小bug的,本文只是提供了一个简单的思路和代码,更多功能可以自己修改代码进行添加。如果不明白这个自定义指令为什么是这样的写法,可以参考我的另一篇文章自定义懒加载图片插件v-lazyload。

上述就是小编为大家分享的如何自定义类似于jQueryUISelectable的Vue指令v-selectable了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

元素 鼠标 区域 选择 定位 位置 功能 代码 思路 指令 虚线 容器 就是 属性 效果 样式 内容 同时 插件 时候 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 无视违法的服务器 广州俏手游软件开发怎么样 创建个人地理数据库 利用文献数据库辅助选题能实现的是什么 网络安全法相关法律汇总 河北麻将软件开发性价比出众 mc基岩版服务器手机电脑 赞皇县委网络安全和信息化 5g网络安全风险报告 信息处理技术员数据库视频 charls数据库 微盘 7月22日网络技术挑战赛 怎么学好网络安全与执法 网络安全问题情侣短袖 服务器厂商维修人员工资 建立网络安全宣传周机制 东莞rpa软件开发公司 服务器安全狗 限制远程 知识产权技术中心数据库 网络安全是一条怎样的赛道 北京金山网络技术有限公司 网页服务器开不了 信令服务器开源项目 pos零售登录数据库失败怎么弄 大同学校触控答题软件开发公司 期货量化分钟数据库 数据库oracle下载不了 开我的世界服务器可以用的映射 qt数据库文档检索软件开发 r星连接服务器很慢
0