js实现鼠标拖拽多选功能

实现功能
在PC端的H5页面中,客户拖动鼠标可以连选多个选项

效果展示
在这里插入图片描述

具体代码如下

<!DOCTYPE html>
<html><head><title>鼠标拖拽多选功能</title><script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script><style type="text/css">* {box-sizing: border-box;}ul {width: 500px;height: auto;margin: 0;padding: 20px;font-size: 0;/*需设置定位*/position: relative;}li {width: 70px;height: 70px;padding: 0;display: inline-block;vertical-align: top;font-size: 13px;border: 1px solid #d9d9d9;}#moveSelected {position: absolute;background-color: blue;opacity: 0.3;border: 1px dashed #d9d9d9;top: 0;left: 0;}.selected {background-color: pink;}</style>
</head><body><ul class="list"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><!-- 鼠标拖拽出的遮罩 (定位为  position:absolute)--><!-- 遮罩最好是在绑定了mouseover事件的元素内部,并且不要阻止遮罩的冒泡事件。这样鼠标移到了遮罩上面,依然可以利用冒泡执行父元素的mouseover事件,就不会出现遮罩只能扩大,不能缩小的情况了(亲自试过) --><div id="moveSelected"></div></ul>
</body></html>
<script type="text/javascript">$(document).ready(function () {let moveSelected = $('#moveSelected')[0];console.log(moveSelected);let flag = false; //是搜开启拖拽的标志let oldLeft = 0; //鼠标按下时的left,toplet oldTop = 0;let selectedList = []; //拖拽多选选中的块集合// 鼠标按下时开启拖拽多选,将遮罩定位并展现$(".list").mousedown(function (event) {flag = true;moveSelected.style.top = event.pageY + 'px';moveSelected.style.left = event.pageX + 'px';oldLeft = event.pageX;oldTop = event.pageY;event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});// 鼠标移动时计算遮罩的位置,宽 高$(".list").mousemove(function (event) {if (!flag) return; //只有开启了拖拽,才进行mouseover操作if (event.pageX < oldLeft) { //向左拖moveSelected.style.left = event.pageX + 'px';moveSelected.style.width = (oldLeft - event.pageX) + 'px';} else {moveSelected.style.width = (event.pageX - oldLeft) + 'px';}if (event.pageY < oldTop) { //向上moveSelected.style.top = event.pageY + 'px';moveSelected.style.height = (oldTop - event.pageY) + 'px';} else {moveSelected.style.height = (event.pageY - oldTop) + 'px';}event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});//鼠标抬起时计算遮罩的right 和 bottom,找出遮罩覆盖的块,关闭拖拽选中开关,清除遮罩数据$(".list").mouseup(function (event) {moveSelected.style.bottom = Number(moveSelected.style.top.split('px')[0]) + Number(moveSelected.style.height.split('px')[0]) + 'px';moveSelected.style.right = Number(moveSelected.style.left.split('px')[0]) + Number(moveSelected.style.width.split('px')[0]) + 'px';findSelected();flag = false;clearDragData();event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});$(".list").mouseleave(function (event) {flag = false;moveSelected.style.width = 0;moveSelected.style.height = 0;moveSelected.style.top = 0;moveSelected.style.left = 0;event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});function findSelected() {let blockList = $('.list').find('li');for (let i = 0; i < blockList.length; i++) {//计算每个块的定位信息let left = $(blockList[i]).offset().left;let right = $(blockList[i]).width() + left;let top = $(blockList[i]).offset().top;let bottom = $(blockList[i]).height() + top;let leftTwo = moveSelected.style.left.split('px')[0]let rightTwo = moveSelected.style.right.split('px')[0]let topTwo = moveSelected.style.top.split('px')[0]let bottomTwo = moveSelected.style.bottom.split('px')[0]// 判断碰撞if (!(top > bottomTwo || right < leftTwo || bottom < topTwo || left> rightTwo)) {// 碰撞的情况selectedList.push(blockList[i]);$(blockList[i]).addClass('selected');}}console.log(selectedList);}function clearDragData() {moveSelected.style.width = 0;moveSelected.style.height = 0;moveSelected.style.top = 0;moveSelected.style.left = 0;moveSelected.style.bottom = 0;moveSelected.style.right = 0;}});
</script>

考虑方便使用Vue框架的情况,新增一个Vue 3的参考代码,且如果多选时处于已经选择状态也会取消选择

//
// MutiSelectPage
// mrs-console-ui
//
// Created by gaolailong on 2024/05/23.
// Copyright © 2024 上海复微迅捷数字科技股份有限公司. All rights reserved.
//<template><div class="page"><div class="list" @mousedown="handleMouseDown" @mousemove="handleMouseMove" @mouseup="handleMouseUp"@mouseleave="handleMouseLeave"><template v-for="(item, index) in list" :key="index"><div class="can-select-div" :class="item.selected ? 'selected' : ''" :data-index="index">{{ item.text }}</div></template><!-- 鼠标拖拽出的遮罩 (定位为  position:absolute)--><!-- 遮罩最好是在绑定了mouseover事件的元素内部,并且不要阻止遮罩的冒泡事件。这样鼠标移到了遮罩上面,依然可以利用冒泡执行父元素的mouseover事件,就不会出现遮罩只能扩大,不能缩小的情况了(亲自试过) --><div id="moveSelected"></div></div></div>
</template><script lang='ts'>
import { defineComponent, onMounted, reactive } from 'vue'
export default defineComponent({name: 'MutiSelectPage',setup() {let moveSelected: HTMLElement | null = nullonMounted(() => {moveSelected = document.getElementById('moveSelected') as HTMLElement;})const list: Array<Record<string, unknown>> = reactive([])for (let index = 0; index < 10; index++) {const listItem = {text: index,selected: false}list.push(listItem)}let flag = false; //是搜开启拖拽的标志let oldLeft = 0; //鼠标按下时的left,toplet oldTop = 0;const handleMouseDown = (event: any) => {// 处理鼠标按下事件console.log('鼠标按下');if (!moveSelected) return;flag = true;moveSelected.style.top = event.pageY + 'px';moveSelected.style.left = event.pageX + 'px';oldLeft = event.pageX;oldTop = event.pageY;event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}const handleMouseMove = (event: any) => {// 处理鼠标移动事件的逻辑if (!moveSelected) return;if (!flag) return; //只有开启了拖拽,才进行mouseover操作console.log("鼠标移动了");if (event.pageX < oldLeft) { //向左拖moveSelected.style.left = event.pageX + 'px';moveSelected.style.width = (oldLeft - event.pageX) + 'px';} else {moveSelected.style.width = (event.pageX - oldLeft) + 'px';}if (event.pageY < oldTop) { //向上moveSelected.style.top = event.pageY + 'px';moveSelected.style.height = (oldTop - event.pageY) + 'px';} else {moveSelected.style.height = (event.pageY - oldTop) + 'px';}event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}const handleMouseUp = (event: any) => {if (!moveSelected) return;// 处理鼠标弹起事件console.log('鼠标弹起');moveSelected.style.bottom = Number(moveSelected.style.top.split('px')[0]) + Number(moveSelected.style.height.split('px')[0]) + 'px';moveSelected.style.right = Number(moveSelected.style.left.split('px')[0]) + Number(moveSelected.style.width.split('px')[0]) + 'px';findSelected();flag = false;clearDragData();event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}const handleMouseLeave = (event: any) => {console.log('鼠标离开元素');if (!moveSelected) return;flag = false;moveSelected.style.width = '0';moveSelected.style.height = '0';moveSelected.style.top = '0';moveSelected.style.left = '0';event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}function findSelected() {if (!moveSelected) return;let leftTwo = Number(moveSelected.style.left.split('px')[0])let rightTwo = Number(moveSelected.style.right.split('px')[0])let topTwo = Number(moveSelected.style.top.split('px')[0])let bottomTwo = Number(moveSelected.style.bottom.split('px')[0])// 使用ref获取dom有一个奇怪的现象,手动切换按天/按周时,ref获取到的值不会丢失之前的数据。除非点击外部的“确认查询”按钮。const blockList = document.getElementsByClassName('can-select-div')// console.log(blockList) // 访问DOM元素for (let i = 0; i < blockList.length; i++) {//计算每个块的定位信息let left = (blockList[i] as HTMLDivElement).offsetLeft;let right = (blockList[i] as HTMLDivElement).offsetWidth + left;let top = (blockList[i] as HTMLDivElement).offsetTop;let bottom = (blockList[i] as HTMLDivElement).offsetHeight + top;// 通过比较两个矩形(用 aDiv 和 bDiv 表示)的位置信息来判断它们是否发生碰撞。以下是该碰撞函数的实现思路:// 首先,通过获取 aDiv 和 bDiv 的位置信息,分别计算它们的上边界(t1 和 t2)、右边界(r1 和 r2)、下边界(b1 和 b2)以及左边界(l1 和 l2)。// 接下来,通过比较这些边界信息,判断两个矩形是否发生碰撞。碰撞的情况可以通过以下四个条件中的任意一个来判断:// 如果矩形 aDiv 的上边界大于矩形 bDiv 的下边界,说明 aDiv 在 bDiv 的上方,没有碰撞。// 如果矩形 aDiv 的右边界小于矩形 bDiv 的左边界,说明 aDiv 在 bDiv 的左侧,没有碰撞。// 如果矩形 aDiv 的下边界小于矩形 bDiv 的上边界,说明 aDiv 在 bDiv 的下方,没有碰撞。// 如果矩形 aDiv 的左边界大于矩形 bDiv 的右边界,说明 aDiv 在 bDiv 的右侧,没有碰撞。// 如果上述条件中的任意一个不满足,那么矩形 aDiv 和 bDiv 就发生了碰撞,函数返回 true,否则返回 false 表示没有碰撞。// 判断碰撞if (!(top > bottomTwo || right < leftTwo || bottom < topTwo || left > rightTwo)) {// 碰撞的情况console.log('碰撞了');const itemIndex = (blockList[i] as HTMLDivElement).dataset.indexif (!itemIndex) {return}const item = list[Number(itemIndex)]item.selected = !item.selectedlist.splice(Number(itemIndex), 1, item)}}}const clearDragData = () => {if (!moveSelected) return;moveSelected.style.width = '0';moveSelected.style.height = '0';moveSelected.style.top = '0';moveSelected.style.left = '0';moveSelected.style.bottom = '0';moveSelected.style.right = '0';}return {handleMouseDown,handleMouseMove,handleMouseUp,handleMouseLeave,list,}}
})
</script><style scoped>
.page {box-sizing: border-box;
}.can-select-div {width: 70px;height: 70px;padding: 0;display: inline-block;vertical-align: top;font-size: 13px;border: 1px solid #d9d9d9;
}#moveSelected {position: absolute;background-color: blue;opacity: 0.3;border: 1px dashed #d9d9d9;top: 0;left: 0;
}.selected {background-color: pink;
}
</style>

参考1、参考2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/838952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ClickHouse配置与使用

静态IP配置 # 修改网卡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33# 修改文件内容 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic IPADDR192.168.18.128 NETMASK255.255.255.0 GATEWAY192.168.18.2 DEFROUTEyes IPV4_FAILURE_FATALno IPV6INIT…

《深入解析:近邻算法的原理、实现与应用》

《深入解析&#xff1a;近邻算法的原理、实现与应用》 引言&#xff1a; 在机器学习和数据挖掘领域&#xff0c;近邻算法&#xff08;k-Nearest Neighbors, k-NN&#xff09;是一种基本且常用的分类与回归方法。其核心思想在于根据近邻的信息进行预测&#xff0c;即通过查询输…

Android 屏保开关

设置-显示-屏保&#xff0c; 打开关闭 设置代码在 ./packages/apps/Settings/src/com/android/settings/dream/DreamMainSwitchPreferenceController.java &#xff0c; Overridepublic boolean isChecked() {return mBackend.isEnabled();}Overridepublic boolean setChecke…

【408真题】2009-12

“接”是针对题目进行必要的分析&#xff0c;比较简略&#xff1b; “化”是对题目中所涉及到的知识点进行详细解释&#xff1b; “发”是对此题型的解题套路总结&#xff0c;并结合历年真题或者典型例题进行运用。 涉及到的知识全部来源于王道各科教材&#xff08;2025版&…

招人啦~数通售后、云计算和云服务的岗位需求

小伙伴们大家好&#xff0c;小誉的就业推荐又来咯。想要跳槽晋升找工作的朋友们&#xff0c;千万不要错过机会哦~ 北京集成商数通售后 薪资:12-18k 1、负责公司系统集成项目的网络技术实施工作&#xff0c;包括项目的网络架构的规划、设计、调整、性能优化; 2、负责从项目开展…

零基础HTML教程(35)--网站的本地部署

文章目录 1. 背景2. 网站的本地部署3. 本地部署的步骤4. 服务器软件介绍5. 本地部署实操5.1 开发一个网站5.2 下载服务器软件5.3 将网站复制到服务器软件下5.4 启动服务器软件5.5 通过Http协议访问网站 6. 小结 1. 背景 我们之前开发的网页&#xff0c;都是编写完成后&#xf…

Sass预处理器相关知识笔记

什么是Sass **Sass&#xff08;Syntactically Awesome Stylesheets&#xff09;**是一种CSS预处理器&#xff0c;它扩展了CSS的功能&#xff0c;使其更加强大和灵活。Sass允许开发者使用变量、嵌套规则、混合&#xff08;mixins&#xff09;、继承等特性&#xff0c;从而更高效…

Day22:Leetcode:654.最大二叉树 + 617.合并二叉树 + 700.二叉搜索树中的搜索 + 98.验证二叉搜索树

LeetCode&#xff1a;654.最大二叉树 1.思路 解决方案&#xff1a; 单调栈是本题的最优解&#xff0c;这里将单调栈题解本题的一个小视频放在这里 单调栈求解最大二叉树的过程当然这里还有leetcode大佬给的解释&#xff0c;大家可以参考一下&#xff1a; 思路很清晰&#xf…

云渲染的线程数是什么意思?

云渲染线程是指在云渲染过程中&#xff0c;同时处理渲染任务的线程数量。 线程是CPU调度和执行的基本单位&#xff0c;每个线程可以独立执行一系列指令。在云渲染场景中&#xff0c;服务器通常配备有高性能的CPU&#xff0c;这些CPU可能拥有几十甚至上百个物理核心&#xff0c…

python多个list组成的list去重 考虑顺序

在Python中&#xff0c;如果你有多个列表组成的列表&#xff0c;并且你想要去除其中的重复元素&#xff0c;同时考虑顺序&#xff0c;你可以使用functools.total_ordering装饰器来简化代码&#xff0c;并使用set来去重。 下面是一个示例代码&#xff1a; from functools impo…

Vue2基础及其进阶面试(一)

简单版项目初始化 新建一个vue2 官网文档&#xff1a;介绍 — Vue.js 先确保下载了vue的脚手架 npm install -g vue-cli npm install -g vue/cli --force vue -V 创建项目 vue create 自己起个名字 选择自己选择特性 选择&#xff1a; Babel&#xff1a;他可以将我们写…

单向无头链表实现

目录 1. 为什么要有链表&#xff1f; 2. 链表的种类 3. 具体功能实现 &#xff08;1&#xff09;节点结构体定义 &#xff08;2&#xff09;申请节点 &#xff08;3&#xff09;尾插 &#xff08;4&#xff09;尾删 &#xff08;5&#xff09;头插 &#xff08;6&#…

SDUT 链表3

7-3 sdut-C语言实验-链表的结点插入 分数 20 全屏浏览 切换布局 作者 马新娟 单位 山东理工大学 给出一个只有头指针的链表和 n 次操作&#xff0c;每次操作为在链表的第 m 个元素后面插入一个新元素x。若m 大于链表的元素总数则将x放在链表的最后。 输入格式: 多组输入。…

【Python设计模式13】抽象工厂模式

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供一个接口&#xff0c;用于创建一系列相关或依赖的对象&#xff0c;而无需指定它们具体的类。抽象工厂模式通过对产品类的抽象&#xff0c;使客户端可以使用抽象工厂来创建…

算法和远程编程题

文章目录 2024心得0504 堆/栈/队列用两个栈实现队列 05 哈希BM52 数组中只出现一次的两个数字 2024 牛客网在线编程 心得 大厂会要求&#xff0c;这个其实没有什么用&#xff0c;就是故意为难人&#xff0c;但是要想找一份工作&#xff0c;这个还是只能去遵守规则。面试造火…

go语言之函数基础

1.介绍 函数是基本的代码块&#xff0c;Go是编译型语言&#xff0c;所以函数编写的顺序是无关紧要的&#xff0c;但是我们一般把main&#xff08;&#xff09;函数写在文件的前面&#xff0c;其他函数按照一定的逻辑顺序编写&#xff08;例如函数被调用顺序&#xff09;。 编写…

PHP的多样化执行方式(parallel PHP多线程实现,原生协程实现,多进程实现,ZTS、NTS、TS又是什么)

进程、线程、协程 进程&#xff1a;应用程序的启动实例&#xff0c;运行起的代码叫进程&#xff0c;有独立的内存空间&#xff0c;类比工厂的P个&#xff08;P1单进程&#xff0c;P>1多进程&#xff09;车间。线程&#xff1a;线程是CPU调度的最小单位&#xff0c;是进程内…

面向对象-----继承

前面向大家介绍了面向对象中的封装性&#xff0c;今天再来向大家介绍面向对象的继承和多态的两大特性。 1.继承 1.1 为什么需要继承&#xff1f; 在java语言中&#xff0c;我们用类来描述世间万物&#xff0c;虽然万物非常复杂&#xff0c;但总有一些共同点&#xff0c;如果…

CAD2023 2024 2025以上版本出现无法运行 AutoCAD,原因可能如下1) 此版本的 AutoCAD 安装不正确

错误提示如下 此版本的 AutoCAD 安装不正确 缺少依赖组件Microsoft Edge webview2 Runtime 缺少依赖组件 Microsoft.NET跟You must install .NET Desktop Runtime 打开autoremove&#xff0c;点击扩展&#xff0c;输入 无法运行&#xff0c;点击搜索 你的软件属于什么版本…

fork 与 vfork 的区别

关键区别一&#xff1a; vfork 直接使用父进程存储空间&#xff0c;不拷贝。 关键区别二&#xff1a; vfork保证子进程先运行,当子进程调用exit退出后&#xff0c;父进程才执行。 我们可以定义一个cnt&#xff0c;在子进程中让它变成3&#xff0c; 如果使用fork&#xff0c;那…