原生JavaScript实现滑动拖动验证的示例代码


Posted in Javascript onDecember 06, 2019

本文介绍了原生JavaScript实现滑动拖动验证的示例代码,分享给大家,具体如下:

原生JavaScript实现滑动拖动验证的示例代码

通常,我们为了防止用户恶意提交表单,会让用户在提交前完成滑动拖动验证,有时候这也能起到一丝反爬的作用。

实现滑动验证的方式当然不止一种,这里我们直接使用原生 JavaScript 来实现。

现在,你可以在这里 看到完整的源码。

原生实现

原生 JavaScript 的实现,主要是通过监听鼠标事件来对 DOM 进行一系列的操作。

滑块验证的结构主要分为四个部分:轨道、滑块、背景和文案,我们可以使用下面的 HTML 结构来表示。

<div class="slide-track">
 <div class="slide-bg"></div>
 <div class="slide-block"></div>
 <p class="slide-text">请按住滑块,拖动到最右边</p>
</div>

基本思路就是我们给滑块(.slide-block)添加相应的事件,在按下滑块时记录鼠标的当前位置并添加滑动事件,在滑动过程中根据鼠标的移动来移动滑块的位置和增加背景元素(.slide-bg)的宽度,直到移动到轨道(.slide-track)的末端后,改变文案(.slide-text)来提示成功。

样式

在开始写脚本之前可以先来完成一下它们的样式,这让滑块相关的部分看起来更好,也让后面的工作更愉快的进行。

/* 样式的注意事项 */

样式的写法就不贴了,相信大家一看就懂,而且会有更好的实现。需要的话,也可以在 Github 上找到。

脚本

现在开始来实现脚本的内容,首先我们对 document.querySelector 方法进行简单的封装以方便后续操作 DOM 元素。

function $(selectors) {
 return document.querySelector(selectors);
}

然后通过自定义的 _h 函数我们可以很方便的创建上面的 HTML 结构,并添加到文档中。

function _h(tagName, propMap = {}, text) {
 const ele = document.createElement(tagName);
 Object.keys(propMap).forEach(prop => ele.setAttribute(prop, propMap[prop]));
 if (text) {
  ele.appendChild(document.createTextNode(text));
 }
 return ele;
}

class SlideUnlock {
 constructor(el = "body", options = {}) {
  this.$el = $(el)
  this.$$isSuccess = false
  this.$options = { // 默认配置
   tip: '请按住滑块,拖动到最右边',
   unlockText: '验证成功',
   duration: 500,
   ...options
  }
 }

 init() {
  this.$$root = _h("div", { class: "slide-track" }) // 轨道
  this.$$bg = this.$$root.appendChild(_h("div", { class: "slide-bg" }))
  this.$$block = this.$$root.appendChild(
   _h("div", { class: "slide-block" }) // 滑块
  )
  this.$$text = this.$$root.appendChild(
   _h("p", { class: "slide-text" }, this.$options.tip)
  )
  this.$el.insertBefore(this.$$root, this.$el.firstChild)
 }
}

在创建好 DOM 结构后,接下来为滑块添加鼠标按下的事件,在这个事件中我们需要记录下鼠标的初始横坐标,以便后续和滑动过程中的位置相比较,同时为其添加滑动事件。

class SlideUnlock {
 init() {
  /* ... */
  this.$$block.addEventListener(
   "mousedown",
   (this.$$handleMouseDown = this._handleMouseDown.bind(this)),
   false
  )
 }

 _handleMouseDown(e) {
  const downx = e.clientX

  e.target.addEventListener(
   "mousemove",
   (this.$$handleMouseMove = this._handleMouseMove.bind(this, downx)),
   false
  )
  e.preventDefault()
 }

 _handleMouseMove(downx, e) {}
}

在这里有点细节需要注意:

  • 首先,由于事件监听器中的 this 指向的是触发事件的元素,为此我们在指定鼠标按下的监听器时为其绑定了 this,以便调用滑块实例属性和原型上的方法。
  • 其次,我们在鼠标按下的监听器中添加了鼠标移动的监听器,如果在初始时同按下的监听器一起指定,那么会先执行鼠标移动的监听器,而此时并没有记录鼠标的初始位置;

接下来我们要实现滑动过程中的主要逻辑:根据鼠标的移动实时地更新滑块的位置,并对一些临界位置进行处理。

_handleMouseMove(downx, e) {
 const info = this.$$block.getBoundingClientRect(),
  x = e.clientX,
  y = e.clientY,
  x1 = info.left,
  y1 = info.top,
  x2 = info.right,
  y2 = info.bottom,
  moveX = x - downx

 if (this.$$isSuccess) {
  return
 }
 if (moveX < 0) {
  return
 }
 if (x < x1 || x > x2 || y < y1 || y > y2) {
  // 当鼠标移开滑块时取消移动
  return
 }

 this.$$block.style.left = `${moveX}px` // 更新滑块的我i之
 this.$$bg.style.width = `${moveX}px` // 同步增大背景元素的宽度

 // 当滑块滑动的距离大于等于轨道除去滑块宽度后的距离时表示已经到达轨道的最右边了
 if (moveX >= this.$$root.offsetWidth - (x2 - x1)) {
  this.$$isSuccess = true
  this.$$text.textContent = "验证成功"
  this.$$text.style.cssText = `color: #fff; left: 0; right: ${this.$$block.offsetWidth}px`
  this.$$block.classList.add("success")
 }
}

这里的实现也很简单,唯一需要看一下的就是通过 getBoundingClientRect 来获取了滑块相对于视口的位置,然后根据鼠标所在的位置来判断鼠标是否在滑块上,如果不在则取消移动。

现在它已经能很好的滑动,并完成提示成功的基本功能了。但是,当我们每次滑动到中间就取消,然后再次点击滑动时,就会导致重复的添加滑动事件,而且中途释放后,滑块就停在了当前位置,这显然不对。

解决的办法就是在添加鼠标按下事件的时候,同时也指定一个松开的事件,在这个事件的监听器中判断如果没有成功则取消之前绑定的滑动事件,并进行重置,为了看起来更友好,我们还可以加上一点动画。

class SlideUnlock {
 init() {
  /* ... */
  document.addEventListener(
   "mouseup",
   (this.$$handleMouseUp = this._handleMouseUp.bind(this)),
   false
  )
 }

 _handleMouseDown(e) {
  /* ... */
  // 取消在手动滑动过程中的动画效果
  this.$$bg.style.transition = ""
  this.$$block.style.transition = ""
  /* ... */
 }

 _handleMouseUp(e) {
  this.$$block.removeEventListener(
   "mousemove",
   this.$$handleMouseMove,
   false
  )

  if (this.$$isSuccess) {
   return
  }

  // 给重置过程添加动画效果
  this.$$bg.style.transition = "width 1s ease"
  this.$$block.style.transition = "left 1s ease"

  this.$$block.style.left = 0
  this.$$bg.style.width = 0
 }
}

目前为止,滑块已经可以在 PC 端正常的工作了,不过在移动端却并不理想。

为了它能够在移动端也可以很好的工作,我们可以借助 touchstarttouchmovetouchend 等事件来完成。

绑定这些事件的时机和处理方式和之前的三个事件分别相对应,所以我们新增两个方法(和 jQuery 的 on、off 方法很像)来更好的添加和移除事件。

function bindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.addEventListener(event, handler, false)
 })
}

function unbindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.removeEventListener(event, handler, false)
 })
}

根据这两个方法,我们来稍微修改一下滑块中添加事件的代码。

function bindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.addEventListener(event, handler, false)
 })
}

function unbindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.removeEventListener(event, handler, false)
 })
}

另外,需要注意的是在移动端 touch 事件中获取 clientXclientY 时不能在事件对象上直接读取,而是在 event.changedTouches[0] 对象上取得。

现在,它已经能够同时在 PC 端和移动端上工作了,不过我们还能对它进行一些优化,比如使用函数节流。

函数节流的实现方式有很多,这里列一下我们在本次过程中使用的方式。

utils.throttle = function(method, context = {}, delay = 4, ...outParams) {
 return function(...innerParams) {
 clearTimeout(context.$$tId)
 context.$$tId = setTimeout(function() {
  method.apply(context, [...outParams, ...innerParams])
 }, delay)
 }
}

然后用这个节流函数,来包装我们移动时的处理函数,并根据实际情况做点调整。

除此之外,我们还可以添加一个重置的方法,让它回到最初的状态,涉及到的内容也很简单,就是在成功的状态下重新设置样式和绑定事件。

reset() {
 if (!this.$$isSuccess) {
  return
 }
 this.$$isSuccess = false
 this.$$bg.style.cssText =
  `transition: width ${this.$options.duration}ms ease; width: 0;`
 this.$$block.style.cssText =
  `transition: left ${this.$options.duration}ms ease; left: 0;`
 this.$$text.style.cssText =
  `color: #5f5f5f; left: ${this.$$block.offsetWidth}px; right: 0;`
 this.$$text.textContent = this.$options.tip
 this.$$block.classList.remove("success")
 this._bindEvents()
}

好了,滑块的实现到这里就告一段落了,相信大家看到这里已经完全明白了,甚至有更好的实现。

如何使用

你可以简单的在 HTML 页面中引入该脚本,然后根据自己的需求设置合适的样式;不过更好的方式是通过这样的思路,在项目中做一些改进(比如平滑降级)等处理。

接下来是一个简单的使用模板。

<body>
 <script src="slide-unlock/core.js"></script>
 <script>
  const slider = new SlideUnlock()
  slider.init()
 </script>
</body>

你可以在 这里 看见完整的使用方式和效果。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript同步Import,同步调用外部js的方法
Jul 08 Javascript
精通Javascript系列之Javascript基础篇
Jun 07 Javascript
jQuery中验证表单提交方式及序列化表单内容的实现
Jan 06 Javascript
js调用后台、后台调用前台等方法总结
Apr 17 Javascript
JS实现图片上传预览功能
Nov 21 Javascript
微信小程序 配置文件详细介绍
Dec 14 Javascript
基于JavaScript实现滑动门效果
Mar 16 Javascript
JavaScript使用Ajax上传文件的示例代码
Aug 10 Javascript
AngularJS实现注册表单验证功能
Oct 16 Javascript
Vue项目查看当前使用的elementUI版本的方法
Sep 27 Javascript
关于layui 实现点击按钮添加一行(方法渲染创建的table)
Sep 29 Javascript
浅谈JavaScript浅拷贝和深拷贝
Nov 07 Javascript
微信小程序 自定义弹窗实现过程(附代码)
Dec 05 #Javascript
Nuxt v-bind绑定img src不显示的解决
Dec 05 #Javascript
微信小程序swiper左右扩展各显示一半代码实例
Dec 05 #Javascript
微信小程序顶部导航栏可滑动并选中放大
Dec 05 #Javascript
微信小程序实现点击按钮后修改颜色
Dec 05 #Javascript
React 父子组件通信的实现方法
Dec 05 #Javascript
Javascript中的this,bind和that使用实例
Dec 05 #Javascript
You might like
php下几个常用的去空、分组、调试数组函数
2009/02/22 PHP
php跨站攻击实例分析
2014/10/28 PHP
php基于curl重写file_get_contents函数实例
2016/11/08 PHP
将string解析为json的几种方式小结
2010/11/11 Javascript
js自执行函数的几种不同写法的比较
2012/08/16 Javascript
IE的fireEvent方法概述及应用
2013/02/22 Javascript
javascript获取鼠标位置部分的实例代码(兼容IE,FF)
2013/08/05 Javascript
可恶的ie8提示缺少id未定义
2014/03/20 Javascript
js中定义一个变量并判断其是否为空的方法
2014/05/13 Javascript
Javascript的表单验证-初识正则表达式
2016/03/18 Javascript
Three.js利用顶点绘制立方体的方法详解
2017/09/27 Javascript
基于 Immutable.js 实现撤销重做功能的实例代码
2018/03/01 Javascript
JS实现关键词高亮显示正则匹配
2018/06/22 Javascript
详解vue使用vue-layer-mobile组件实现toast,loading效果
2018/08/31 Javascript
微信小程序进入广告实现代码实例
2019/09/19 Javascript
vue 关闭浏览器窗口的时候,清空localStorage的数据示例
2019/11/06 Javascript
[46:20]CHAOS vs Alliacne 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
python pickle 和 shelve模块的用法
2013/09/16 Python
python实现QQ空间自动点赞功能
2019/04/09 Python
python中bs4.BeautifulSoup的基本用法
2019/07/27 Python
django实现模板中的字符串文字和自动转义
2020/03/31 Python
python 决策树算法的实现
2020/10/09 Python
Python常用外部指令执行代码实例
2020/11/05 Python
深入理解css属性的选择对动画性能的影响
2016/04/20 HTML / CSS
利用CSS3制作简单的3d半透明立方体图片展示
2017/03/25 HTML / CSS
浅析HTML5页面元素及属性
2021/01/20 HTML / CSS
全球最大的在线橄榄球商店:Lovell Rugby
2018/05/20 全球购物
Top Villas美国:豪华别墅出租和度假屋
2018/07/10 全球购物
汇源肾宝广告词
2014/03/20 职场文书
城管大队整治方案
2014/05/06 职场文书
2014年路政工作总结
2014/12/10 职场文书
2016自主招生教师推荐信范文
2015/03/23 职场文书
2019预备党员转正申请书模板2篇!
2019/08/07 职场文书
导游词之黄帝陵景区
2019/09/16 职场文书
七年级作文之我的梦想
2019/10/16 职场文书
SQL Server中使用判断语句(IF ELSE/CASE WHEN )案例
2021/07/07 SQL Server