前置知识
导航
[封装01-设计模式] 设计原则 和 工厂模式(简单抽象方法) 适配器模式 装饰器模式
[封装02-设计模式] 命令模式 享元模式 组合模式 代理模式
[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署
[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] koa
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend
[源码-vue06] Vue.nextTick 和 vm.$nextTick
[源码-react01] ReactDOM.render01
[源码-react02] 手写hook调度-useState实现
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI
[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例
[深入24] Fiber
[深入25] Typescript
[深入26] Drag
[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
[前端学java06-SpringBoot实战] 注入 + Swagger2 3.0 + 单元测试JUnit5
[前端学java07-SpringBoot实战] IOC扫描器 + 事务 + Jackson
[前端学java08-SpringBoot实战总结1-7] 阶段性总结
[前端学java09-SpringBoot实战] 多模块配置 + Mybatis-plus + 单多模块打包部署
[前端学java10-SpringBoot实战] bean赋值转换 + 参数校验 + 全局异常处理
[前端学java11-SpringSecurity] 配置 + 内存 + 数据库 = 三种方式实现RBAC
[前端学java12-SpringSecurity] JWT
[前端学java13-SpringCloud] Eureka + RestTemplate + Zuul + Ribbon
(1) 一些单词
illegal 非法的
notation 符号 标记法
identifier 标识符
efficiency 效率
notification 通知 // notify 通知
draggable 拖动
destination 目的地
verification 验证 // verification code 验证码
annotation 注释 注解
(2) 拖动事件
- 触发先后顺序
- dragstart -> dragenter -> dragover -> dragleave -> drop -> dragend
- 开始拖拽 -> 拖拽进入 -> 拖拽经过 -> 拖拽离开 -> 拖拽释放 -> 拖拽结束
- 需要阻止 ( 默认行为 ) 的两个方法
- dragover 和 drop
dragover 和 drop 事件需要e.preventDefault()阻止默认方法,事件中后续代码才会生效
(3) 鼠标点击相关事件
- 触发先后顺序
- mousedown -> mousemove -> mouseup
- 鼠标按下 -> 鼠标移动 -> 鼠标释放
- 双击鼠标左键时,触发的事件顺序如下
- mousedown -> mouseup -> click -> mousedown -> mouseup -> click -> dblclick
(4) mouseenter,mouseleave 和 mouseover,mouseout 的区别?
- 配对
- mouserenter 和 mouserleave 配对
- mouserover 和 mouserout 配对
- 进入
- mouseenter
- mouseover
- 离开
- mouseleave
- mouseout
- 具有冒泡性质的是两个带o的事件,进入子元素后事件冒泡到父元素
- mouseover
- mouseout
- 当进入绑定事件的元素的子元素内部时,mouserover会触发,mouseenter不会触发
- 当离开绑定事件的元素的子元素内部时,mouserout会触发,mouseleave不会触发
(5) DataTransfer 对象
- 作用
DataTransfer对象用来保存 (拖动并放下) (drag and drop) 过程中的数据- 它可以保存 ( 一项或多项数据 ),这些数据项可以是 ( 一种或者多种数据类型 )
- 获取
DataTransfer对象可以从 (所有拖动事件) 的 (dataTransfer) 属性上获取
- 构造函数
- dataTransfer = new DataTransfer()
- 生成并且返回一个新的
DataTransfer对象
- 5个标准属性
- DataTransfer.dropEffect
- 获取当前所选拖放操作的类型,或将拖拽操作设置为新类型
- 值必须为
none,copy,link或move中的一个 - dropEffect属性顾名思意拖拽效果,在PC web端主要表现在 ( 鼠标手形 ) 上
- DataTransfer.effectAllowed
- 提供可能的所有类型的操作
- 枚举类型:
none,copy,copyLink,copyMove,link,linkMove,move,all或uninitialized- none 不允许拖拽,鼠标保持禁止状态
- copyLink 允许复制和链接操作
- copyMove 允许复制和移动操作
- linkMove 允许链接和移动操作
- all 什么操作都允许
- DataTransfer.files
- 拖拽的本地文件列表
- 如果拖动操作不涉及拖动文件,则此属性为空列表
- DataTransfer.items
- 供DataTransferItemList对象,该对象是所有拖动数据的列表
- 只读
- DataTransfer.types
- 在
dragstart事件中设置数据格式,返回的是一个字符串数组 - 只读
- 在
- DataTransfer.dropEffect
- 4个标准方法
- DataTransfer.getData(format)
- getData 快捷获取拖拽的内容
- 获取纯文本:text/plain
- 实际开发中,一般直接使用 text
- DataTransfer.setData(format, data)
- 重新设置被拖拽的内容
- 可以重置原生的拖拽内容,或者用来参数传递
- 实际开发拖拽具体模块元素,此时
setData()多用来传递拖拽元素的id
- DataTransfer.clearData([format])
clearData() 只能用在 dragstart 事件中- 作用:清除所有数据,即执行clearData()后getData()只能活到到空字符串
- 注意:
- clearData() 只能用在 dragstart 事件中
- 当我们需要使用setData()自定义拖拽数据的时,为了避免原生拖拽数据干扰,可以先执行一次clearData()方法
- DataTransfer.setDragImage(img, offsetX, offsetY)
setDragImage() 只能用在 dragstart 事件中- 作用:设置拖拽时候有个图片跟在后面
- 参数
- img:表示图表DOM元素对象
- offsetX:距离鼠标的水平偏移距离
- offsetY:距离鼠标的垂直偏移距离
- DataTransfer.getData(format)
(6) scrollTop,offsetTop,scrollHeight,clientHeight,offsetHeight
- scrollTop 和 offsetTop
scrollTop- 表示有滚动条时,滚动条 ( 向下垂直滚动的距离 )
offsetTop- ( 当前元素顶部 ) 距离 ( 最近父元素顶部 ) 的距离
- offsetTop和有没有滚动条无关,是一个固定值,滚动时值不会改变
- 当元素 display:none 时,offsetTop = 0
- clientHeight 和 offsetHeight
clientHeight- 包括:( padding )
- 不包括:( border ),( margin ),( 水平滚动条 )
offsetHeight- 包括:( padding ),( border ),( 水平滚动条 )
- 不包括:( margin )
- 所以:offsetHeight = client Height + border
- scrollHeight
scrollHeight = clientHeight + scrollTop
(7) JSON - review
(7.1) 基本概念
- 类型和格式的要求
- 原始类型的值
- 只能是
number string boolean null - 不能是 (
undefined)
- 只能是
- 复合类型的值
- 只能是
对象和数组 - 不能是 (
函数,日期对象,正则对象)
- 只能是
- 字符串必须是
双引号,对象的key必须有双引号 - 对象和数组的最后一个属性不能有
,
- 原始类型的值
- 空数组,空对象,null,都是合法的json数据
- 静态方法,一共有两个静态方法
JSON.stringify()JSON.parse()
(7.2) JSON.stringify()
- JSON.stringify(value[, replacer [, space]])
- 作用
- 用于将一个
值转为JSON 字符串 - 该字符串符合 JSON 格式,并且可以被
JSON.parse方法还原
- 用于将一个
对于原始类型的字符串,转换结果会带双引号
JSON.stringify('abc') === 'abc' // false
JSON.stringify('abc') === "abc" // false
JSON.stringify('abc') === "'abc'" // false,注意单引号和双引号的顺序,上面的就是返回false
JSON.stringify('abc') === '"abc"' // true
JSON.stringify('abc') === "\"abc\"" // true
// 这是因为将来还原的时候,内层双引号可以让 JavaScript 引擎知道,这是一个字符串,而不是其他类型的值
// 请再看下面的例子
JSON.stringify(false) // "false"
JSON.stringify('false') // ""false""
// 上面代码中,如果不是内层的双引号,将来还原的时候,引擎就无法知道原始值是布尔值还是字符串。
JSON.stringify() 会忽略 ( 对象的不可遍历属性 - 即不可枚举属性 )如果 ( 对象的属性 ) 是 ( undefined 或 函数 ),则会被 JSON.stringify() 过滤如果 ( 数组的成员 ) 是 ( undefined 或 函数 ),则会被 JSON.stringify() 转成 null- undefined或function -> 对象过滤,数组转null
对象
---
const obj = {
a: undefined,
b: function () {},
};
Object.defineProperties(obj, {
c: {
value: "ccc",
enumerable: true, // 可枚举
},
d: {
value: "ddd",
enumerable: false, // 不可枚举
},
});
JSON.stringify(obj); // '{"c":"ccc"}'
// 1. 如果对象的属性是 ( undefined 或 function ) 则会被 JSON.stringify() 过滤
// 2. JSON.stringify() 会忽略对象的 ( 不可枚举的属性,不可遍历的属性 )
数组
---
var arr = [undefined, function () {}];
JSON.stringify(arr) // "[null,null]"
// 如果数组的成员是 ( undefined 或 function ) 则会被 JSON.stringify() 转成 null
- 参数
- 第二个参数
- 如果接受一个数组,指定需要转成字符串的属性。
- 如果接受一个函数,用来更改
JSON.stringify的返回值
- 第三个参数
JSON.stringify还可以接受第三个参数,用于增加返回的 JSON 字符串的可读性- 如果是
数字,表示每个属性前面添加的空格(最多不超过10个) - 如果是
字符串,(不超过10个字符),则该字符串会添加在每行前面。
- 第二个参数
(7.3) JSON.parse()
- 作用
JSON.parse方法用于将 JSON 字符串转换成对应的值- 如果传入的字符串不是有效的 JSON 格式,
JSON.parse方法将报错
- 参数
- 第二个参数
- 接受一个
处理函数,用来更改返回值
- 接受一个
- 第二个参数
(一) 实现拖动 - 方法一
- 并不是所有元素都是默认可以拖动的
图片是默认可以拖动的 - 所以一般如果图表不要拖动的话,会设置draggable=falsedraggable=true在元素标签上添加上该属性后,元素就可以被拖动
- 案例
- 方块:绿色小方块,蓝色小方块,红色大方块
- 移动性:绿色小方块可以移动,而蓝色小方块不能移动
- 需求
- 绿色小方块可以拖动,蓝色块不能拖动
- 移入
- 绿色小方块移入红色大方块中时,变成红色大方块的最后一个子元素
- 文字提示:当前方块名
- 移出:
- 绿色小方块移出红色大方块时,红色块清除绿色块,绿色块复原位
- 文字提示复原
实现代码
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.wrap {
border: 1px solid black;
}
.destination-content {
width: 100px;
height: 100px;
border: 1px solid red;
}
.origin1,
.origin2 {
width: 50px;
height: 50px;
border: 1px solid green;
}
.origin2 {
border: 1px solid blue;
}
</style>
</head>
<body>
<div class="wrap">
<p class="destination-title">当前元素 -></p>
<div class="destination-content" />
</div>
<div class="origin2" data-name="blue_box"></div>
<div class="origin1" data-name="green_box" draggable="true"></div>
<script>
let name = null;
let dragDOM = null;
const wrapDOM = document.querySelector("div.wrap");
const destinationContent = document.querySelector(
"div.destination-content"
);
const destinationTitle = document.querySelector("p.destination-title");
console.log(`destinationTitle`, destinationTitle);
document.addEventListener(
"dragstart",
(e) => {
name = e.target.getAttribute("data-name");
dragDOM = e.target;
},
false
);
document.addEventListener(
"drag",
(e) => {
e.target.style.background = "green";
destinationContent.style.background = "red";
},
false
);
document.addEventListener(
"dragend",
(e) => {
e.target.style.background = "#fff";
destinationContent.style.background = "#fff";
},
false
);
destinationContent.addEventListener(
"dragenter",
(e) => {
e.target.style.background = "black";
destinationTitle.innerHTML = "当前元素 ->" + " " + name;
},
false
);
destinationContent.addEventListener("dragover", (e) => {
e.preventDefault();
// 注意:
// 1. 这里一定要添加 dragover 事件
// 2. 也一定要阻止 默认行为
// 不然,下面的 drop 方法将没有效果
});
destinationContent.addEventListener(
"dragleave",
(e) => {
e.target.removeChild(dragDOM);
wrapDOM.appendChild(dragDOM);
destinationTitle.innerHTML = "当前元素 ->";
},
false
);
destinationContent.addEventListener(
"drop", // drop 方法是绑定在 destinationContent 上的,这点一定要注意
(e) => {
console.log(`555`, 555);
e.preventDefault(); // 必须阻止默认行为,后面的添加child代码才会生效显示在页面上
console.log(`6666`, dragDOM);
e.target.appendChild(dragDOM);
destinationTitle.innerHTML = "当前元素 ->" + " " + name;
},
false
);
</script>
</body>
</html>
(二) 实现拖动 - 验证码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.content {
position: relative;
}
.small {
position: absolute;
left: 0;
top: 90px;
cursor: move;
transform: scale(0.9);
}
</style>
</head>
<body>
<div class="verification">
<div class="title">验证码</div>
<div class="content">
<!-- 这里两张图片的 draggable=false 表示图片不会被拖动,即防止被拖动,我们这里不是用drag属性和事件去实现的 -->
<img src="./images/big.jpg" alt="b" draggable="false" class="big" />
<img src="./images/small.png" alt="s" draggable="false" class="small" />
</div>
</div>
<script>
// 验证码
const title = document.querySelector("div.title");
const big = document.querySelector("img.big");
const small = document.querySelector("img.small");
const bigLeft = big.offsetLeft; // big图片左上角 距离 父元素的左上角 的 ( 水平位移 ) 距离 ------------------------------ 该值固定的 - 无论是小方块移动或者静止,所以可以用任意时刻的其他值计算,所以可以用const
const bigTop = big.offsetTop; // big图片左上角 距离 父元素的左上角 的 ( 垂直位移 ) 距离 ------------------------------- 该值固定的 - 无论是小方块移动或者静止,所以可以用任意时刻的其他值计算,所以可以用const
// const smallLeft = small.offsetLeft; // small图片左上角 距离 父元素(big)的左上角 的 ( 水平位移 ) 距离 ------------------ 该值鼠标点击时固定,小方块移动时不固定
// const smallTop = small.offsetTop; // small图片左上角 距离 父元素(big)的左上角 的 ( 垂直位移 ) 距离 -------------------- 该值鼠标点击时固定,小方块移动时不固定
small.addEventListener(
"mousedown",
(e) => {
const mouseToSmallLeft = e.pageX - bigLeft - small.offsetLeft; // 鼠标点击位置 距离 small 的水平位移 ------------------- 该值固定的 - 无论是小方块移动或者静止,所以可以用任意时刻的其他值计算,所以可以用const
const mouseToSmallTop = e.pageY - bigTop - small.offsetTop; // 鼠标点击位置 距离 small 的垂直位移 ---------------------- 该值固定的 - 无论是小方块移动或者静止,所以可以用任意时刻的其他值计算,所以可以用const
const updateUi = (e) => {
if (
e.pageX > 447 &&
e.pageX < 492 &&
e.pageY > 174 &&
e.pageY < 218
) {
title.innerHTML = "验证码验证成功";
title.style.background = "green";
title.style.width = 568 + "px";
title.style.color = "white";
} else {
title.innerHTML = "验证码";
title.style.background = "";
}
};
const moving = (e) => {
const mouseLeft = e.pageX; // 鼠标 距离 页面的 ( 水平位移 )
const mouseTop = e.pageY; // 鼠标 距离 页面的 ( 垂直位移 )
small.style.left = mouseLeft - bigLeft - mouseToSmallLeft + "px";
small.style.top = mouseTop - bigTop - mouseToSmallTop + "px";
updateUi(e);
};
// 注意:这里是 document 监听的 mousemove
document.addEventListener("mousemove", moving, false);
document.addEventListener(
"mouseup",
(e) => {
document.removeEventListener("mousemove", moving, false);
},
false
);
},
false
);
</script>
</body>
</html>
鼠标点击位置距离黄块的距离 = 鼠标距离页面的距离pageX - 绿色块距离页面的距离 - 黄色块距离绿色块的距离
---
最终要计算的是 - 白色的offsetLeft
---
白色的offsetLeft = 鼠标位置 - 黑色offsetLeft - 鼠标距离黄色块的距离
(三) 实现拖动 - 文字拖动
- 需求
- 拖动文字,先修改被选中的要拖动的文字,再去掉除数字外的其他字符
- 拖动时,显示一张图片
- 关键点
e.dataTransfer.getData('text')e.dataTransfer.setData('text', '替换成什么文字')drop事件需要在监听函数中阻止默认行为,通过 e.preventDefault()- drop 和 dragover 一般都要设置e.preventDefault()
- 过程
- 1.
选中文字的p标签,使之可以拖动 - 2.将p标签拖入input框,监听
input框的drop事件 - 3.在
drop事件中获取到e.dataTransfer.getData('text')- 即可获取到 ( 选中部分的文字 )
- 注意:是选中部分的文字,不是所有的文字,在第1步中有操作选中哪些文字
- 1.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>138-8888-9999-文字</p>
<input type="text" />
<script>
// 文字拖动
// https://www.zhangxinxu.com/wordpress/2018/09/drag-drop-datatransfer-js/
const input = document.querySelector("input");
const img = new Image();
img.src = "./images/small.png";
// 1
// DataTransfer.setData()
// DataTransfer.setDragImage(img, offsetX, offsetY)
document.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text", "133-4444-5555-文字"); // 将 ( 138-8888-9999-文字 ) 替换成 ( 133-4444-5555-文字 )
e.dataTransfer.setDragImage(img, 10, 10); // 拖拽时显示一张图片
});
// 2
// DataTransfer.getData()
input.addEventListener("drop", function (e) {
// 注意是:input 的 drop 事件
e.preventDefault(); // drop 和 dragover 需要 e.preventDefault() 后面的代码才会生效
var selectedText = e.dataTransfer.getData("text"); // 获取拖拽纯文本内容(被选取部分)
input.value = selectedText.replace(/\D/g, ""); // 清除所有非数字字符,将其替换成 ''
});
</script>
</body>
</html>
资料
JS拖动对象那些事 juejin.cn/post/684490…
1小时搞定卡片拖拽 juejin.cn/post/684490…
张鑫旭 www.zhangxinxu.com/wordpress/2…