前端魔法堂:屏蔽Backspace导致页面回退
前言
前几天用户反映在录入资料时一不小心错按Backspace键,就会直接回退到是一个页面,导致之前辛辛苦苦录入的资料全部丢失了。哦?居然还有这种情况。下面我们来一起探讨一下吧!
Windows系统下独有的行为
Windows下的IE、FireFox和Chrome 52之前的浏览器,当焦点不在一个可编辑的元素上时,按Backspace
键就会回退到上一个页面,按Shift
+Backspace
键则会前进到下一个页面。
而Chrome 52以后的浏览器则屏蔽了Backspace
和Shift
+Backspace
的上述行为,而是采用Alt
+Left
实现回退和Alt
+Right
实现前进。如果想恢复Backspace
回退,则需要安装Go Back With Backspace的Extension才行。
对于FireFox而言,我们可以设置Backspace
和Shift
+Backspace
的行为。
- 在地址栏输入
about:config
- 在搜索框输入
browser.backspace_action
,然后设置项目值即可。有3个可选项
0
,表示Backspace
和Shift
+Backspace
的行为对应页面回退和前进(Windows下的默认值)
1
,表示Backspace
和Shift
+Backspace
的行为对应页面向下滚动和向上滚动
2
或其他值,表示不响应Backspace
和Shift
+Backspace
(Ubuntu16下的默认值)
注意:Linux和OS X下的浏览器按Backspace
和Shift
+Backspace
不会触发页面的回退和前进。
如何应对
方案一:页面跳转时弹出二次确认
通过beforeunload
事件实现页面跳转时弹出二次确认模态窗,让用户有后悔的机会。但会截断其他正常跳转的操作流畅性,在确实没有办法时才使用!
方案二:直接屏蔽
屏蔽Backspace
和Shift
+Backspace
的默认行为,仅当焦点落在可编辑区域中时才暂时取消屏蔽。
那么哪些算是能获得焦点的可编辑区域呢?就下面这些咯!!
input[type=text]:not([readonly])
input[type=password]:not([readonly])
input[type=number]:not([readonly])
input[type=email]:not([readonly])
input[type=url]:not([readonly])
input[type=search]:not([readonly])
input[type=tel]:not([readonly])
textarea:not([readonly])
[contenteditable]:not([readonly])
就是说当焦点落在上述符合规则的元素上时,按Backspace
和Shift
+Backspace
的默认行为就不是页面跳转,因此不用屏蔽掉。
附加功能
现在我们的目的是页面不会因为用户误操作而刷新,导致页面数据丢失。这里有两个组合键同样会的导致页面刷新
ctrl
+r
刷新当前页面,可被阻止;ctrl
+w
关闭当前窗体或标签页,无法阻止。
代码时间.js
;window.nobsgb || (function(exports){
var started = false
exports.start = function(){started = true}
exports.stop = function(){started = false}
var KEYCODE = {
BACKSPACE: 8,
R: 82
}
// 判断type是否不受阻止
var isEscapableType = function(rEscapableTypes){
return function(type){
return rEscapableTypes.test(type)
}
}(/text|textarea|tel|email|number|search|password|url/i)
// 判断标签是否不受阻止
var isEscapableTag = function(rEscapableTag){
return function(tag){
return rEscapableTag.test(tag)
}
}(/input|textarea/i)
// 判断是否设置为content editable
var isContentEditable = function(el){
return el.isContentEditable
}
// 判断是否为不受阻止的Backspace
var isEscapableBackspace = function(el){
return or(isEscapableTag(el.tagName)
&&
or(!('type' in el)
, ('type' in el) && isEscapableType(el.type) && !el.readOnly)
, isContentEditable(el))
}
var isCtrlR = function(e, keycode){
return e.ctrlKey && KEYCODE.R === keycode
}
var isArray = function(x){
return /Array/.test(Object.prototype.toString.call(x))
}
var getEvt = function(e){
return e || window.event
}
var getTarget = function(e){
return e.target || e.srcElement
}
var getKeycode = function(e){
return e.keyCode || e.which
}
var preventDefault = function(e){
e.preventDefault && e.preventDefault()
e.returnValue = false
return false
}
var listen = function(listen){
return function(evtNames, handler){
if (!isArray(evtNames)){
evtNames = [evtName]
}
var i = 0
, len = evtNames.length
for (; i < len; ++i){
listen(evtNames[i], handler)
}
}
}(function(evtName, handler){
if (or(document['addEventListener'] && (document['addEventListener'].apply(document, arguments) || true)
, document['attachEvent'] && (document['attachEvent'].apply(document, arguments) || true))){
document['on'+evtName] = handler
}
})
var or = function(){
var ret = false
, i = 0
, len = arguments.length
for (; !ret && i < len; ++i){
ret = ret || arguments[i]
}
return ret
}
var handler = function(e){
if (!started) return true
var evt = getEvt(e)
, el = getTarget(evt)
, keyCode = getKeycode(evt)
if (or(KEYCODE.BACKSPACE === keyCode && !isEscapableBackspace(el)
, isCtrlR(evt, keyCode))){
return preventDefault(evt)
}
}
listen(['keydown'], handler)
}(window.nobsgb = {}))
代码时间.cljs
core.cljs
(ns nobsgb.core
(:require [nobsgb.dom :as dom]
[nobsgb.pred :as pred]))
(def started false)
(defn ^:export start []
(set! started true))
(defn ^:export stop []
(set! started false))
(defn handler
"keydown事件响应函数"
[e]
(when started
(let [evt (dom/get-evt e)
el (dom/get-el evt)
key-code (dom/get-key-code evt)
ctrl-key (dom/get-ctrl-key evt)
read-only (dom/get-read-only el)
type (dom/get-type el)
content-editable (dom/get-content-editable el)
tag (dom/get-tag el)]
(if-not
(pred/escapable?
key-code read-only type tag content-editable ctrl-key)
(dom/prevent-default evt)
true))))
(defonce init
(#(dom/listen! js/document "keydown" handler)))
dom.cljs
(ns nobsgb.dom)
(defn get-evt
[e]
(if (some? e) e (.event js/window)))
(defn get-el
[e]
(let [el (.-target e)]
(if (some? el) el (.-srcElement e))))
(defn get-key-code
[e]
(.-keyCode e))
(defn get-ctrl-key
[e]
(.-ctrlKey e))
(defn get-read-only
[el]
(-> el (aget "readOnly") js/Boolean))
(defn get-type
[el]
(let [type (.-type el)]
(if (some? type) type "")))
(defn get-tag
[el]
(.-tagName el))
(defn get-content-editable
[el]
(.-isContentEditable el))
(defn prevent-default
[e]
(if (some? (.-preventDefault e))
(do
(.preventDefault e)
(set! (.-returnValue e) false)
false)
true))
(defn listen!
[el evt-name handler]
(cond
(fn? (.-addEventListener el)) (.addEventListener el evt-name handler)
(fn? (.-attachEvent el)) (.attachEvent el (str "on" evt-name) handler)
:else (aset el (str "on" evt-name) handler)))
pred.cljs
(ns nobsgb.pred)
;;;; 断言
(defonce ^:const KEYCODES
{:backspace 8
:r 82})
(defn matches-key?
"是否匹配指定键码"
[indicated-key-code key-code]
(= indicated-key-code key-code))
(def ^{:doc "是否为退格键"}
backspace?
(partial matches-key? (:backspace KEYCODES)))
(def ^{:doc "是否为字母R键"}
r?
(partial matches-key? (:r KEYCODES)))
(defn with-ctrl?
"是否在按ctrl的基础上按其他键"
[ctrl-key]
(or (= ctrl-key "1")
(true? ctrl-key)))
(defn ctrl+r?
"是否为ctrl+r"
[ctrl-key key-code]
(and (with-ctrl? ctrl-key)
(r? key-code)))
(def not-ctrl+r? (complement ctrl+r?))
(defn escapable-type?
"是否为可跳过的type属性"
[type]
(some?
(some->> type
(re-matches #"(?i)text|password|tel|number|email|search|url"))))
(defn escapable-tag?
"是否为可跳过的tag"
[tag]
(some?
(some->> tag
(re-matches #"(?i)input|textarea"))))
(def ^{:doc "是否设置为可编辑元素"}
content-editable? identity)
(def ^{:doc "是否设置为只读"}
read-only? identity)
(def writable? (complement read-only?))
(defn escapable-backspace?
[key-code read-only type tag content-editable]
(and (backspace? key-code)
(writable? read-only)
(or (escapable-type? type)
(escapable-tag? tag)
(content-editable? content-editable))))
(defn escapable?
[key-code read-only type tag content-editable ctrl-key]
(or
(and (not-ctrl+r? ctrl-key key-code)
(not (backspace? key-code)))
(escapable-backspace? key-code read-only type tag content-editable)))
总结
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/6890699.html _肥仔John
前端魔法堂:屏蔽Backspace导致页面回退的更多相关文章
- 前端魔法堂:解秘FOUC
前言 对于问题多多的IE678,FOUC(flash of unstyled content)--浏览器样式闪烁是一个不可忽视的话题,但对于ever green的浏览器就不用理会了吗?下面尝试较全面 ...
- 前端魔法堂:可能是你见过最详细的WebWorker实用指南
前言 JavaScript从使用开初就一直基于事件循环的单线程运行模型,即使是成功进军后端开发的Nodejs也没有改变这一模型.那么对于计算密集型的应用,我们必须创建新进程来执行运算,然后执行进程间通 ...
- 前端魔法堂:onsubmit和submit事件处理函数怎么不生效呢?
前言 最近在用Polymer增强form,使其支持表单的异步提交,但发现明明订阅了onsubmit和submit事件,却怎么也触发不了.下面我们将一一道来. 提交表单的方式 表单仅含一个以下的元素时 ...
- 前端javascript如何阻止按下退格键页面回退 但 不阻止文本框使用退格键删除文本
这段代码可以: document.onkeydown = function (e) { e.stopPropagation(); // 阻止事件冒泡传递 e.preventDefault(); // ...
- 【JS】【1】JavaScript屏蔽Backspace键(避免点击后页面产生回退)
前言: 1,参考资料:JavaScript屏蔽Backspace键 - 孤傲苍狼 - 博客园(http://www.cnblogs.com/xdp-gacl/p/3785806.html) 2,参考的 ...
- CSS魔法堂:更丰富的前端动效by CSS Animation
前言 在<CSS魔法堂:Transition就这么好玩>中我们了解到对于简单的补间动画,我们可以通过transition实现.那到底多简单的动画适合用transtion来实现呢?答案就是 ...
- 阻止按下backspace键造成页面回退相像
在IE浏览器中,会出现当你使用鼠标选中input标签或者是textarea标签,或者啥也没选中的时候,按下backspace键会触发浏览器的回退. 针对以上问题的解决思路: 1. 当按下键盘时 ...
- HTML5魔法堂:全面理解Drag & Drop API
一.前言 在HTML4的时代,各前端工程师为了实现拖拽功能可说是煞费苦心,初听HTML5的DnD API觉得那些痛苦的日子将一去不复返,但事实又是怎样的呢?下面我们一起来看看DnD API的真面 ...
- CSS魔法堂:说说Float那个被埋没的志向
前言 定位系统中第一难理解就是Normal flow,而第二就非Float莫属了,而Float难理解的原因有俩,1. 一开头我们就用错了:2. 它跟Normal flow靠得太近了.本文尝试理清Fl ...
随机推荐
- 老李分享: JSON
老李分享: JSON poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478, ...
- 老李分享:robotium3.6与4.0 later 的区别 2
再仔细看了下4.0中的方法: java.util.ArrayList<android.view.View> getCurrentViews() Returns an ...
- 裴波那序列-JAVA实现
编程输出:裴波那序列,1000项,int会越界! BigInteger [] pArr=new BigInteger [10000]; pArr[0]=new BigIntege ...
- EasyMvc--让MVC区域开发更Easy(提供源码下载)
核心: 主要利用MVC的区域功能,实现项目模块独立开发和调试. 目标: 各个模块以独立MVC应用程序存在,即模块可独立开发和调试. 动态注册各个模块路由. 一:新建解决方案目录结构 如图: 二:Eas ...
- Spark源码分析之分区器的作用
最近因为手抖,在Spark中给自己挖了一个数据倾斜的坑.为了解决这个问题,顺便研究了下Spark分区器的原理,趁着周末加班总结一下~ 先说说数据倾斜 数据倾斜是指Spark中的RDD在计算的时候,每个 ...
- php checkbox 从数据库读取和写入
checkbox将选中的值写入数据库中,在修改的时候如何从数据库中读取并设定Checkbox的状态 1.写入数据库提交后因为你的rol是数组,所以可以使用$_POST获取 PHP code ? 1 ...
- 【Java基础】Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...
- 1.2 N层架构
N层架构 介绍 ABP架构 其他(通用) 领域层 应用层 基础设施层 网络和展现层 其他 总结 介绍 应用程序代码库的分层架构是被广泛认可的可以减少程序复杂度.提高代码复用率的技术.为了实现分层架构, ...
- java接收数据接口
1.数据接收接口: 这个可以考虑最简单的Servlet方法,而且效率较高: import java.io.PrintWriter;import java.text.SimpleDateFormat;i ...
- Python、PyCharm的安装及使用方法(Mac版)
上周跟朋友喝咖啡时聊起我想学Python,她恰好也有这个打算,顺便推荐了一本书<编程小白的第1本Python入门书>,我推送到Kindle后,随手翻看了下,用语平实,简洁易懂. 之前在R语 ...