如何防止按下退格键后返回上一页?

290

在IE中,我可以使用(非常不标准但有效的)jQuery实现此操作

if ($.browser.msie)
    $(document).keydown(function(e) { if (e.keyCode == 8) window.event.keyCode = 0;});

但是有没有可能以一种适用于Firefox的方式或者实现跨浏览器的方式呢?

记录一下:

$(document).keydown(function(e) { if (e.keyCode == 8) e.stopPropagation(); });

不做任何事情。

$(document).keydown(function(e) { if (e.keyCode == 8) e.preventDefault(); });
解决了问题,但造成了页面上不能使用退格键的问题,这甚至比原来的行为更糟糕。
编辑: 我这么做的原因是我不是在创建一个简单的网页,而是在创建一个大型应用程序。仅仅因为你在错误的位置按了一下退格键就会丢失10分钟的工作时间,这实在是太让人恼火了。避免错误与打扰用户的比例应该远高于1000/1,通过禁止退格键导航回去来防止这种情况。
编辑2:我并不想阻止历史记录导航,只是想要防止意外发生。
编辑3:@brentonstrines评论(将其移到此处,因为问题如此流行):这是一个长期的“修复”方法,但您可以支持Chromium bug以更改webkit中的此行为

142
由于退格键功能过载,你可能没有注意到,但你可能经常使用它来删除刚刚在文本框中键入的字母。我的一些客户因为这个问题而感到困扰,导致页面返回,所以这是有用的信息。除了你们这些小丑之外,没人知道退格键应该返回上一页。这是我从未知道的事情,对于浏览器来说,这是完全敌对的行为。我认为所有页面都应该禁用这种行为。 - Breton
83
为什么人们认为这很奇怪?使用退格键进行导航是一个非常愚蠢的快捷方式!有很多文本字段用户可能想要删除文本——想象一下有一个很长的stackoverflow答案,在切换到另一个窗口查看东西后,你回来时误点了编辑区域,按下退格键删除一个单词,突然浏览器返回上一页,你可能会丢失你刚刚写的所有内容。 - Peter Boughton
59
为什么我想这样做呢?我不是在创建一个网站而是一个网络应用程序。一些输入字段是只读的,看起来像可编辑的,但如果您按退格键,您将离开该页面。想要导航回去的退格键次数与试图删除某些内容的退格键次数的比率可能远小于1/1000。 - erikkallen
43
问题是如何做到这一点,而不是你对此是否是个好主意的看法。如果不知道该项目的详细信息,你的意见是没有意义的。我的客户特别要求在我的一个项目中实现这种行为,一个真正的答案而不是“你为什么要这样做?”会更有帮助。 - None
7
这是一个长期的“修复”计划,但你可以支持Chromium bug来更改webkit中的这种行为:http://code.google.com/p/chromium/issues/detail?id=144832 - brentonstrine
显示剩余13条评论
33个回答

1
一种更整洁的解决方案 -
$(document).on('keydown', function (e) {
    var key = e == null ? event.keyCode : e.keyCode;
    if(key == 8 && $(document.activeElement.is(':not(:input)')))   //select, textarea
      e.preventDefault();
});

或者,您只需检查是否

$(document.activeElement).is('body')

1

性能?

我担心性能问题并创建了一个演示:http://jsfiddle.net/felvhage/k2rT6/9/embedded/result/

var stresstest = function(e, method, index){...

我分析了这个帖子中最有前途的方法。结果发现,它们都非常快,很可能在打字时不会导致“迟缓”的问题。 我看到的最慢的方法是在IE8中进行10,000次调用大约需要125毫秒。这是每个按键0.0125毫秒。
我发现Codenepal和Robin Maben发布的方法最快~ 0.001ms(IE8),但要注意不同的语义。
也许对于引入此类功能到他的代码的人来说,这是一种解脱。

1

纯JavaScript版本,在所有浏览器中都可使用:

document.onkeydown = function(e) {stopDefaultBackspaceBehaviour(e);}
document.onkeypress = function(e) {stopDefaultBackspaceBehaviour(e);}

function stopDefaultBackspaceBehaviour(event) {
  var event = event || window.event;
  if (event.keyCode == 8) {
    var elements = "HTML, BODY, TABLE, TBODY, TR, TD, DIV";
    var d = event.srcElement || event.target;
    var regex = new RegExp(d.tagName.toUpperCase());
    if (regex.test(elements)) {
      event.preventDefault ? event.preventDefault() : event.returnValue = false;
    }
  }
}

当然,您可以使用“INPUT,TEXTAREA”,然后使用“if(!regex.test(elements))”进行判断。第一个对我来说很有效。

1

修改后的erikkallen答案:

$(document).unbind('keydown').bind('keydown', function (event) {

    var doPrevent = false, elem;

    if (event.keyCode === 8) {
        elem = event.srcElement || event.target;
        if( $(elem).is(':input') ) {
            doPrevent = elem.readOnly || elem.disabled;
        } else {
            doPrevent = true;
        }
    }

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

1
完美的答案。我从其他人那里得到的解决方案在Firefox中没有起作用,但这个解决方案在所有三个浏览器(Firefox、IE和Chrome)中都完美地运行,没有任何问题。谢谢Prozi。 - Nikhil Dinesh

1
使用Dojo工具包1.7,在IE 8中可以正常运行:

require(["dojo/on", "dojo/keys", "dojo/domReady!"],
function(on, keys) {
    on(document.body,"keydown",function(evt){if(evt.keyCode == keys.BACKSPACE)evt.preventDefault()});
});

1
这是我改写的最受欢迎答案。我尝试检查element.value!== undefined(因为一些元素,如可能没有html属性但可能在原型链上的某个位置具有javascript值属性),但是这并没有很好地工作,并且有许多边缘情况。似乎没有很好的方法来未雨绸缪,因此白名单似乎是最佳选择。
这将在事件冒泡阶段的末尾注册元素,因此,如果您想以任何自定义方式处理退格键,则可以在其他处理程序中这样做。
这也检查HTMLTextAreElement的实例,因为理论上可以从中继承的Web组件。
这不检查contentEditable(与其他答案结合使用)。

https://jsfiddle.net/af2cfjc5/15/

var _INPUTTYPE_WHITELIST = ['text', 'password', 'search', 'email', 'number', 'date'];

function backspaceWouldBeOkay(elem) {
    // returns true if backspace is captured by the element
    var isFrozen = elem.readOnly || elem.disabled;
    if (isFrozen) // a frozen field has no default which would shadow the shitty one
        return false;
    else {
        var tagName = elem.tagName.toLowerCase();
        if (elem instanceof HTMLTextAreaElement) // allow textareas
            return true;
        if (tagName=='input') { // allow only whitelisted input types
            var inputType = elem.type.toLowerCase();
            if (_INPUTTYPE_WHITELIST.includes(inputType))
                return true;
        }   
        return false; // everything else is bad
    }
}

document.body.addEventListener('keydown', ev => {
    if (ev.keyCode==8 && !backspaceWouldBeOkay(ev.target)) {
        //console.log('preventing backspace navigation');
        ev.preventDefault();
    }
}, true); // end of event bubble phase

1

我使用当前被接受的(erikkallen的版本)干净的代码版本创建了一个NPM项目。

https://github.com/slorber/backspace-disabler

它基本上使用相同的原则,但有以下不同之处:
  • 无依赖性
  • 支持contenteditable
  • 更易读/易于维护的代码库
  • 将得到支持,因为它将被我的公司用于生产
  • 采用MIT许可证

var Backspace = 8;

// See https://dev59.com/EmnWa4cB1Zd3GeqP3r-a
function addHandler(element, type, handler) {
    if (element.addEventListener) {
        element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + type, handler);
    } else {
        element["on" + type] = handler;
    }
}
function removeHandler(element, type, handler) {
    if (element.removeEventListener) {
        element.removeEventListener(type, handler, false);
    } else if (element.detachEvent) {
        element.detachEvent("on" + type, handler);
    } else {
        element["on" + type] = null;
    }
}




// Test wether or not the given node is an active contenteditable,
// or is inside an active contenteditable
function isInActiveContentEditable(node) {
    while (node) {
        if ( node.getAttribute && node.getAttribute("contenteditable") === "true" ) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}



var ValidInputTypes = ['TEXT','PASSWORD','FILE','EMAIL','SEARCH','DATE'];

function isActiveFormItem(node) {
    var tagName = node.tagName.toUpperCase();
    var isInput = ( tagName === "INPUT" && ValidInputTypes.indexOf(node.type.toUpperCase()) >= 0 );
    var isTextarea = ( tagName === "TEXTAREA" );
    if ( isInput || isTextarea ) {
        var isDisabled = node.readOnly || node.disabled;
        return !isDisabled;
    }
    else if ( isInActiveContentEditable(node) ) {
        return true;
    }
    else {
        return false;
    }
}


// See https://dev59.com/ynI_5IYBdhLWcg3wK_zE
function disabler(event) {
    if (event.keyCode === Backspace) {
        var node = event.srcElement || event.target;
        // We don't want to disable the ability to delete content in form inputs and contenteditables
        if ( isActiveFormItem(node) ) {
            // Do nothing
        }
        // But in any other cases we prevent the default behavior that triggers a browser backward navigation
        else {
            event.preventDefault();
        }
    }
}


/**
 * By default the browser issues a back nav when the focus is not on a form input / textarea
 * But users often press back without focus, and they loose all their form data :(
 *
 * Use this if you want the backspace to never trigger a browser back
 */
exports.disable = function(el) {
    addHandler(el || document,"keydown",disabler);
};

/**
 * Reenable the browser backs
 */
exports.enable = function(el) {
    removeHandler(el || document,"keydown",disabler);
};

0

另一种使用jQuery的方法

    <script type="text/javascript">

    //set this variable according to the need within the page
    var BACKSPACE_NAV_DISABLED = true;

    function fnPreventBackspace(event){if (BACKSPACE_NAV_DISABLED && event.keyCode == 8) {return false;}}
    function fnPreventBackspacePropagation(event){if(BACKSPACE_NAV_DISABLED && event.keyCode == 8){event.stopPropagation();}return true;}

    $(document).ready(function(){ 
        if(BACKSPACE_NAV_DISABLED){
            //for IE use keydown, for Mozilla keypress  
            //as described in scr: http://www.codeproject.com/KB/scripting/PreventDropdownBackSpace.aspx
            $(document).keypress(fnPreventBackspace);
            $(document).keydown(fnPreventBackspace);

            //Allow Backspace is the following controls 
            var jCtrl = null;
            jCtrl = $('input[type="text"]');
            jCtrl.keypress(fnPreventBackspacePropagation);
            jCtrl.keydown(fnPreventBackspacePropagation);

            jCtrl = $('input[type="password"]');
            jCtrl.keypress(fnPreventBackspacePropagation);
            jCtrl.keydown(fnPreventBackspacePropagation);

            jCtrl = $('textarea');
            jCtrl.keypress(fnPreventBackspacePropagation);
            jCtrl.keydown(fnPreventBackspacePropagation);

            //disable backspace for readonly and disabled
            jCtrl = $('input[type="text"][readonly="readonly"]')
            jCtrl.keypress(fnPreventBackspace);
            jCtrl.keydown(fnPreventBackspace);

            jCtrl = $('input[type="text"][disabled="disabled"]')
            jCtrl.keypress(fnPreventBackspace);
            jCtrl.keydown(fnPreventBackspace);
        }
    }); 

    </script>

请注意,如果已动态添加控件(文本框),则此方法无效。 - CodeNepal

0
对于任何感兴趣的人,我已经编写了一个jQuery插件,它结合了thetoolman(加上@MaffooClock/@cdmckay的评论)和@Vladimir Kornea上面的想法。
用法:
//# Disable backspace on .disabled/.readOnly fields for the whole document
$(document).disableBackspaceNavigation();

//# Disable backspace on .disabled/.readOnly fields under FORMs
$('FORM').disableBackspaceNavigation();

//# Disable backspace on .disabled/.readOnly fields under #myForm
$('#myForm').disableBackspaceNavigation();

//# Disable backspace on .disabled/.readOnly fields for the whole document with confirmation
$(document).disableBackspaceNavigation(true);

//# Disable backspace on .disabled/.readOnly fields for the whole document with all options
$(document).disableBackspaceNavigation({
    confirm: true,
    confirmString: "Are you sure you want to navigate away from this page?",
    excludeSelector: "input, select, textarea, [contenteditable='true']",
    includeSelector: ":checkbox, :radio, :submit"
});

插件:

//# Disables backspace initiated navigation, optionally with a confirm dialog
//#     From: https://dev59.com/ynI_5IYBdhLWcg3wK_zE
$.fn.disableBackspaceNavigation = function (vOptions) {
    var bBackspaceWasPressed = false,
        o = $.extend({
            confirm: (vOptions === true),   //# If the caller passed in `true` rather than an Object, default .confirm accordingly,
            confirmString: "Are you sure you want to leave this page?",
            excludeSelector: "input, select, textarea, [contenteditable='true']",
            includeSelector: ":checkbox, :radio, :submit"
        }, vOptions)
    ;

    //# If we are supposed to use the bConfirmDialog, hook the beforeunload event
    if (o.confirm) {
        $(window).on('beforeunload', function () {
            if (bBackspaceWasPressed) {
                bBackspaceWasPressed = false;
                return o.confirmString;
            }
        });
    }

    //# Traverse the passed elements, then return them to the caller (enables chainability)
    return this.each(function () {
        //# Disable backspace on disabled/readonly fields
        $(this).bind("keydown keypress", function (e) {
            var $target = $(e.target /*|| e.srcElement*/);

            //# If the backspace was pressed
            if (e.which === 8 /*|| e.keyCode === 8*/) {
                bBackspaceWasPressed = true;

                //# If we are not using the bConfirmDialog and this is not a typeable input (or a non-typeable input, or is .disabled or is .readOnly), .preventDefault
                if (!o.confirm && (
                    !$target.is(o.excludeSelector) ||
                    $target.is(o.includeSelector) ||
                    e.target.disabled ||
                    e.target.readOnly
                )) {
                    e.preventDefault();
                }
            }
        });
    });
}; //# $.fn.disableBackspaceNavigation

0

到目前为止,所有给出的答案都集中在将修复脚本化到网页中,但是如果我只想为自己添加此功能而不影响其他用户怎么办?

在这种情况下,最好选择浏览器本身的解决方案:
- Linux上的Firefox自2006年以来“取消映射”了退格键行为,因此不受影响;(无论如何,在那之前它只是被设置为向上滚动)
- Chrome刚刚宣布从现在开始也会这样做;(http://forums.theregister.co.uk/forum/1/2016/05/20/chrome_deletes_backspace/
- Windows上的Firefox可以通过进入about:config并将backspace_action设置更改为2来忽略退格键;(http://kb.mozillazine.org/Browser.backspace_action
- Safari ?!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接