原生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 相关文章推荐
jquery ui resizable bug解决方法
Oct 26 Javascript
jqPlot 基于jquery的画图插件
Apr 26 Javascript
使用javascript创建快捷方式的简单实例
Aug 09 Javascript
原始XMLHttpRequest方法详情回顾
Nov 28 Javascript
深入理解jQuery中live与bind方法的区别
Dec 18 Javascript
跟我学习javascript的函数调用和构造函数调用
Nov 16 Javascript
jquery判断密码强度的验证代码
Apr 22 Javascript
jQuery实现简洁的导航菜单效果
Nov 23 Javascript
javascript self对象使用详解
Oct 18 Javascript
微信小程序 教程之wxapp 视图容器 view
Oct 19 Javascript
手把手搭建安装基于windows的Vue.js运行环境
Jun 12 Javascript
JS实现获取word文档内容并输出显示到html页面示例
Jun 23 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实现Ftp用户的在线管理的代码
2007/03/06 PHP
MySQL GBK→UTF-8编码转换
2007/05/24 PHP
phpmyadmin3 安装配置图解教程
2012/03/29 PHP
PHP的异常处理类Exception的使用及说明
2012/06/13 PHP
ThinkPHP使用UTFWry地址库进行IP定位实例
2014/04/01 PHP
Yii使用CLinkPager分页实例详解
2014/07/23 PHP
php保存信息到当前Session的方法
2015/03/16 PHP
PHP中isset与array_key_exists的区别实例分析
2015/06/02 PHP
PHP与以太坊交互详解
2018/08/24 PHP
Yii框架ACF(accessController)简单权限控制操作示例
2019/04/26 PHP
基于laravel Request的所有方法详解
2019/09/29 PHP
css动画效果之animation的常用样式
2021/03/09 HTML / CSS
JQuery 学习笔记 选择器之五
2009/07/23 Javascript
JQuery动态创建DOM、表单元素的实现代码
2011/08/09 Javascript
javascript的回调函数应用示例
2014/02/20 Javascript
JavaScript实现同步于本地时间的动态时间显示方法
2015/02/02 Javascript
javascript无刷新评论实现方法
2015/05/13 Javascript
javascript父子页面通讯实例详解
2015/07/17 Javascript
jQuery判断checkbox选中状态
2016/05/12 Javascript
实例讲解JavaScript中instanceof运算符的用法
2016/06/08 Javascript
js简单判断flash是否加载完成的方法
2016/06/21 Javascript
使用伪命名空间封装保护独自创建的对象方法
2016/08/04 Javascript
json2.js 入门教程之使用方法与实例分析
2017/09/14 Javascript
vue2.0在没有dev-server.js下的本地数据配置方法
2018/02/23 Javascript
webpack结合express实现自动刷新的方法
2019/05/07 Javascript
[17:00]DOTA2 HEROS教学视频教你分分钟做大人-帕克
2014/06/10 DOTA
python使用7z解压软件备份文件脚本分享
2014/02/21 Python
使用pdb模块调试Python程序实例
2015/06/02 Python
Python排序搜索基本算法之希尔排序实例分析
2017/12/09 Python
python中reader的next用法
2018/07/24 Python
Python如何截图保存的三种方法(小结)
2020/09/01 Python
Html+Css+Jquery实现左侧滑动拉伸导航菜单栏的示例代码
2020/03/17 HTML / CSS
机械专业求职信范文
2014/07/15 职场文书
人力资源职位说明书
2014/07/29 职场文书
元旦标语大全
2014/10/09 职场文书
《用字母表示数》教学反思
2016/02/17 职场文书