防止页面退格键回退

1,156 阅读5分钟

cover.png

在一个网页中,当我们在输入框内输入文字或者进行密码输入时,我们并不希望用户通过按下退格键来操作历史记录和回退到上一个页面。因此,我们往往需要编写代码来阻止这个默认行为。本文将介绍如何使用JS来实现这个过程,并展示此方法可以在不同的场景下调用。

什么是退格键

最初的Unix终端是Teletype ASR33,由纸带穿孔机以及纸带阅读机和键盘组成。Backspace和Delete并不属于该键盘上的按键

在键入内容的时候如果出错,则需要按下^(ctrl)+H向teletype发送一个退格命令,使得穿孔机移回之前的位置,然后按下Rubout键再发送一个删除命令才能做到删除对应位置内容

之后为了方便开始制造带有backspace按键的终端,同时完成退格和删除两个功能

后来Rubout键被改名为Delete,一些Unix公司决定在键盘中加入Delete键(用于删除一个字符),这些公司决定用Delete代替^+H代表退格。因此,情况变成了一些键盘可以按下Backspace删除字符,另一些要按下Delete。

对于现在的键盘来讲,实际情况是有Backspace键时按下后则会触发退格加删除两个操作,此时的Delete键的作用只对应删除这一个操作,因此Backspace键删除的都是光标之前的字符,Delete键删除的是光标之后的字符;如果没有Backspace键则用Delete键来代替(也是退格加删除两个操作)。

思路概述

首先,通过键盘事件检查用户的按键,判断对于的键是否是“退格键”。如果是,则阻止默认行为,即不允许回退。该过程可以分为以下几步:

  1. 注册键盘事件监听器;
  2. 获取当前被按下的键名称;
  3. 判断是否对应“退格键”;
  4. 阻止事件的默认行为。

具体实现

document.onkeydown = function(event) {
  var e = event || window.event;
  var keyCode = e.keyCode || e.which;

  // 判断是否为退格键
  if (keyCode == 8) {
    if (e.preventDefault) {
      e.preventDefault();
    } else {
      e.returnValue = false;
    }
  }
}

以上代码展示了具体实现过程。其中,我们通过keyCodewhich获取当前键位,使用if语句判断是否为退格键(退格键的keycode为8),如果是则执行preventDefault()阻止默认行为,否则放行。

兼容性

该方法与不同的浏览器兼容性良好。以上代码中处理退格键的keyCode属性已被所有现代浏览器所支持,同时也被IE等老旧浏览器所支持。

但是,为了保证屏幕能够实时响应用户的操作,我们还应该考虑到一些其他因素。例如:

  • 输入框焦点问题;
  • 操作历史记录问题(比如在Firefox上,无法通过上述方式堵住history.back())。

处理焦点问题

因为我们只想阻止用户在当前输入框输入时使用退格键进行“回退”操作,因此如果用户焦点不在一个输入框内(例如按下Tab键)而按下退格键,我们可以不做任何处理。下面展示了如何根据焦点状态来判断是否进行回退处理。(在本例中,我们选择使用jQuery库)

$(document).on('keydown', function (event) {
  if (event.keyCode == 8) {
    var doPrevent = false;
    var target = event.target;

    if (target.isContentEditable || 
      (target.nodeName.toLowerCase() == 'input' && 
        (target.type.toLowerCase() == 'text' || target.type.toLowerCase() == 'password' || target.type.toLowerCase() == 'email'))) {
      // 在输入框或可编辑区域
      doPrevent = false;
    } else {
      doPrevent = true;
    }

    if (doPrevent) {
      event.preventDefault();
    }
  }
});

处理history.back()问题

如果用户使用退格键作为history.back()方法的替代品,那么我们需要捕获该事件并进行处理。在这种情况下,我们不应该阻止跳转,而是在回退前弹出一个警告框,让用户来确认是否想要撤销操作。

$(document).on("keydown", function(event) {
    var doPrevent = false;

    if ((event.ctrlKey || event.metaKey) && (event.keyCode == 78 || event.keyCode == 82)) {
        doPrevent = true;
    }

    if (event.keyCode == 8) {
        var target = event.target;
        if (target.isContentEditable || 
          (target.nodeName.toLowerCase() == 'input' && 
            (target.type.toLowerCase() == 'text' || target.type.toLowerCase() == 'password' || target.type.toLowerCase() == 'email'))) {
            // 在输入框或可编辑区域
            doPrevent = false;
        } else {
            doPrevent = true;
        }
    }

    if (doPrevent) {
        event.preventDefault();
        window.history.back();

        if (typeof window.history.back.callback === "function") {
            window.history.back.callback();
        } else {
            window.history.back.callStack = window.history.back.callStack || [];
            window.history.back.callStack.push(window.history.back);
        }

        setTimeout(function() {
            var r = confirm("确认要现在离开该页面吗?");
            if (r == true) {
                window.location = "/logout";
            } else {
                window.history.forward();
                if (window.history.back.callStack.length > 0) {
                    var c = window.history.back.callStack.pop();
                    c();
                }
            }
        }, 100);

        return true;
    }
});

如上代码所示,我们将用户的操作记录压入一个callStack数组中,并弹出确认框来防止过快跳转影响用户体验。如果用户选择“确认”,则返回到主页并进行相关处理;否则,程序将在call stack数组中找到最有一个未执行的回调函数,再次执行。

在 Vue 中使用

方案一:自定义指令

Vue v-on 指令支持警告修饰符 .prevent 来停止事件冒泡并防止热键。我们可以创建一个自定义指令来触发该修饰符。

main.js 文件中注册指令:

Vue.directive('prevent-back', {
  bind: function (el, binding, vnode) {
    var handler = function (event) {
      if (event.keyCode === 8) {
        event.preventDefault();
        vnode.elm.dispatchEvent(new Event('input'));
      }
    };

    el.addEventListener('keydown', handler);
    el.addEventListener('blur', function() {
      el.removeEventListener('keydown', handler);
    });
  }
});

在模板中使用自定义指令:

<template>
  <div>
    <input type="text" v-prevent-back />
  </div>
</template>

方案二:Vue Mixin

Vue Mixin 可以让我们在组件中混入同样的数据和方法,从而复用代码。我们可以创建一个全局的 Mixin,在需要阻止回退键的组件中混入该 Mixin。

main.js 文件中定义 Mixin:

Vue.mixin({
  methods: {
    preventBackspace: function(event) {
      if (event.keyCode === 8) {
        event.preventDefault();
      }
    },
    addKeyListener: function() {
      window.addEventListener('keydown', this.preventBackspace, true);
    },
    removeKeyListener: function() {
      window.removeEventListener('keydown', this.preventBackspace, true);
    }
  }
})

在组件中混入 Mixin:

import Vue from 'vue';

export default {
  mixins: [Vue.mixin],
  mounted() {
    this.addKeyListener();
  },
  destroyed() {
    this.removeKeyListener();
  }
}

总结

本文详细介绍了如何实现屏蔽页面退格键回退的方法。同时,我们讨论了如何在不同环境下确保其兼容性和正确性。通过使用jQuery库和一些看似简单但微不足道的技巧,我们成功地在几个关键点上解决了各种问题。我相信读完这篇文章后,你已经能够轻松地在其他网站上使用此方法并应用到自己的项目中去啦!