zoukankan      html  css  js  c++  java
  • 前端魔法堂:屏蔽Backspace导致页面回退

    前言

     前几天用户反映在录入资料时一不小心错按Backspace键,就会直接回退到是一个页面,导致之前辛辛苦苦录入的资料全部丢失了。哦?居然还有这种情况。下面我们来一起探讨一下吧!

    Windows系统下独有的行为

     Windows下的IE、FireFox和Chrome 52之前的浏览器,当焦点不在一个可编辑的元素上时,按Backspace键就会回退到上一个页面,按Shift+Backspace键则会前进到下一个页面。
     而Chrome 52以后的浏览器则屏蔽了BackspaceShift+Backspace的上述行为,而是采用Alt+Left实现回退和Alt+Right实现前进。如果想恢复Backspace回退,则需要安装Go Back With Backspace的Extension才行。
     对于FireFox而言,我们可以设置BackspaceShift+Backspace的行为。

    1. 在地址栏输入about:config
    2. 在搜索框输入browser.backspace_action,然后设置项目值即可。有3个可选项
      0,表示BackspaceShift+Backspace的行为对应页面回退和前进(Windows下的默认值)
      1,表示BackspaceShift+Backspace的行为对应页面向下滚动和向上滚动
      2或其他值,表示不响应BackspaceShift+Backspace(Ubuntu16下的默认值)

    注意:Linux和OS X下的浏览器按BackspaceShift+Backspace不会触发页面的回退和前进。

    如何应对

    方案一:页面跳转时弹出二次确认

     通过beforeunload事件实现页面跳转时弹出二次确认模态窗,让用户有后悔的机会。但会截断其他正常跳转的操作流畅性,在确实没有办法时才使用!

    方案二:直接屏蔽

     屏蔽BackspaceShift+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])
    

    就是说当焦点落在上述符合规则的元素上时,按BackspaceShift+Backspace的默认行为就不是页面跳转,因此不用屏蔽掉。

    附加功能

     现在我们的目的是页面不会因为用户误操作而刷新,导致页面数据丢失。这里有两个组合键同样会的导致页面刷新

    1. ctrl+r刷新当前页面,可被阻止;
    2. 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

  • 相关阅读:
    gitlab10+版本删除项目工程
    Ubuntu16.04上通过anaconda3离线安装Tensorflow2.0详细教程
    android中根据smartRefeshLayout自定义上下拉刷新效果
    android高德地图根据经纬度添加标注(Marker)
    android设置状态栏颜色
    android复制内容到剪贴板
    android拨打电话的两种方式
    java集合转String数组
    解决jenkins部署TomcatManagerException问题
    SpringCloud 微服务消费者之间相互调用的方式
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/6890699.html
Copyright © 2011-2022 走看看