【问题标题】:Javascript event.stopPropagation() doesn't work with `accesskey` attributeJavascript event.stopPropagation() 不适用于 `accesskey` 属性
【发布时间】:2016-07-19 21:35:45
【问题描述】:

一个简单的 html 页面只包含两个控件,一个文本框和一个按钮。页面加载后,如果用户在文本框内单击并按“alt”+p(按钮访问键),则消息应显示为“I'm from key down!!!”但是,如果用户单击文本框内以外的任何位置,则消息应显示为“仅当焦点位于文本框外时才应调用我!!!”。完整代码如下:

<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script type="text/javascript">
    function keyDown() {
        if (event.altKey && event.keyCode == 80) {
            event.returnValue = false;
            event.cancelBubble = true;
            event.keyCode = 0;
            alert("I'm from key down!!!");
        }
    }

    function clickMe() {
        alert("I should be called only when the focus is outside the textbox!!!");
    }
 </script>
</head>
<body>

<div>
    <input type="text" onkeydown ="keyDown();" />
    <input type="button" value="Click me" accesskey="p" onclick="clickMe();" />
</div>

</body>
</html>

在 IE10 及以下版本中运行良好。但它在 IE11 和 Chrome 中不起作用,而是一个接一个地显示两条警报消息,例如“我从按键按下!!!”和“只有当焦点在文本框之外时才应该调用我!!!”这是不可取的。所以 keyDown() 事件处理程序被更改为支持 IE10+ 和 Chrome 之类的

function keyDown() {
     if (event.altKey && event.keyCode == 80) {
        event.preventDefault ? event.preventDefault() : (event.returnValue = false);
        event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true);
        event.keyCode = 0;
        alert("I'm from key down!!!");
     }
}

但是在这种情况下 event.stopPropagation() 不起作用,知道为什么吗?

【问题讨论】:

  • 你怎么知道stopPropagation不起作用?
  • 因为两条警报消息都被触发了。
  • “因为两条警报消息都被触发了。” 只有一个事件处理程序正在侦听keydown 事件。停止传播意味着不会触发附加到 &lt;input&gt; 元素的 ancestor 的另一个 keydown 事件处理程序。也许与其告诉我们什么“行不通”,不如解释一下你想做什么。
  • 在文本框外聚焦不应触发 clickMe(),因为它只绑定到按钮的点击事件。
  • @YoannM: event 在 IE 和 Chrome 中是全局的。

标签: javascript html browser


【解决方案1】:

accesskey 事件总是被触发

(除非您暂时禁用它)

大多数浏览器总是会触发绑定到accesskey属性的事件,即使其他绑定的事件处理程序使用常见的策略例如return falsestopPropagationstopImmediatePropagationpreventDefaultcancelBubble等,在正常情况下,它们可以有效地阻止事件冒泡和默认行为。

但在您的情况下,您有一个处理程序检查您通过accesskey 绑定到另一个元素的相同组合键。因此,每次在文本字段具有焦点时输入组合键时,都会触发两个处理程序:首先在 keyDown 处理程序中,然后无论任何尝试阻止该事件冒泡,都会触发 clickMe 处理程序,因为它是由accesskey激活。

一种解决方案是在您不想听accesskey 属性时(例如当您的文本输入有焦点时)暂时删除它们,然后在您不想再忽略它们时恢复它们(例如当您的文本输入失去焦点时)。

请参阅this answer for a jQuery powered solution,您可以将其用作跳板来创建满足您需求的纯 JS 解决方案。

示例:

<script type="text/javascript">
  
  /* Function to cache accesskey attributes */
  function cacheAccessKeys() {
    
    /* Get all elements with accesskeys
    // This could be modified to select a smaller subset of elements */
    var akEls = document.querySelectorAll('[accesskey]');
    
    /* Iterate over each element in the set of matched elements */
    Array.prototype.forEach.call(akEls, function (el, i) {
      
      /* Set the value of data-accesskey to the value of accesskey */
      el.setAttribute('data-accesskey', el.getAttribute('accesskey'));
      
      /* Remove the accesskey attribute
      // to temporarily disable accesskey binding */
      el.removeAttribute('accesskey');
    });
  }

  /* Function to restore accesskey attributes */
  function restoreAccessKeys() {
    
    /* Get all elements with accesskeys
    // This could be modified to select a smaller subset of elements */
    var akEls = document.querySelectorAll('[data-accesskey]');
    
    /* Iterate over each element in the set of matched elements */
    Array.prototype.forEach.call(akEls, function (el, i) {
      
      /* Set the value of accesskey to the value of data-accesskey
      // to restore accesskey binding */
      el.setAttribute('accesskey', el.getAttribute('data-accesskey'));
      
      /* Clean-up (perhaps unnecessary) 
      // In case the accesskey attributes are set dynamically elsewhere,
      // this prevents mismatched caching. */
      el.removeAttribute('data-accesskey');
    });
  }

  function keyDown(e) {
    if (e.altKey && e.keyCode === 80) {
      console.log("I'm from key down!!!");
    }
  }

  function clickMe() {
    console.log("I should be called only when the focus is outside the textbox!!!");
  }
</script>

<div>
  
  
  <!-- On focus: Cache/remove accesskey attributes -->
  <!-- On keydown: Now the key combo in here won't trigger other handlers. -->
  <!-- On blur: Restore accesskey attributes -->
  <input type="text"
         onfocus="cacheAccessKeys();"
         onkeydown="keyDown(event);"
         onblur="restoreAccessKeys();"
         />
         
  <!-- On click: Activated by Alt + p access key combo
  //-- only when text field does NOT have focus. -->
  <input type="button"
         accesskey="p"
         value="Click me"
         onclick="clickMe();"
         />
</div>

【讨论】:

  • 它在 Chrome 中有效,但在 IE10 中无效。我同意这种做法。谢谢!。
【解决方案2】:

accesskey 属性似乎是问题所在。 Chrome 和 IE 使用 ALT+accesskey 来激活按键。 https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey。删除它,问题就会消失。

jsFiddle example

【讨论】:

  • 谢谢!但是用户总是喜欢“alt”+p而不是鼠标点击“Click Me”按钮(IE10及以下),现在我们支持IE11和Chrome,所以我们不能删除“alt”+p。
  • 好吧,只要你包含,你就会遇到这个问题。
  • 同意,我认为您将不得不检查 document.activeElement
【解决方案3】:

这是另一种解决方法,只需禁用已启用的 accesskey 元素,然后重新启用它。

    function keyDown() {
        if (event.altKey && event.keyCode == 80) {
          var accecskeyObj = $("input[accesskey=" + event.key + "][disabled != disabled]");
         accecskeyObj.prop('disabled',true);
         alert("I'm from key down!!!");
         accecskeyObj.prop('disabled',false);
        }
    }

http://jsfiddle.net/2efgbde1/1/

【讨论】:

    【解决方案4】:

    你没有传递事件对象。

    function keyDown(e) {
    
         var e = event || window.event;
         if (e.altKey && e.keyCode == 80) {
            e.preventDefault ? e.preventDefault() : (e.returnValue = false);
            e.stopPropagation ? e.stopPropagation() : (e.cancelBubble = true);
            e.keyCode = 0;
         }
    }
    

    但是,既然它是全局定义的,那应该不是问题。我检查并添加了任何类型的访问密钥,chrome 绕过两个警报框背靠背。

    要解决这个问题,我认为您除了删除访问密钥之外别无其他选择。删除它后,它在 chrome 和 IE 上显示相同的结果。

    <input type="text" onkeydown="keyDown();" />
    <input type="button" value="Click me" onclick="clickMe();" />
    
    function keyDown() {
        if (event.altKey && event.keyCode == 80) {
            event.returnValue = false;
            event.cancelBubble = true;
            event.keyCode = 0;
            alert("I'm from key down!!!");
        }
    }
    
    function clickMe() {
        alert("I should be called only when the focus is outside the textbox!!!");
    }
    

    【讨论】:

    • 虽然传递事件对象是一种很好的做法,但在这里不会有什么不同,因为event 在 IE 和 Chrome 中是全局的。无论 OP 的问题是什么,这都不是解决方案。
    • @FelixKling 哦。我认为这可能会导致问题,因为它非常清晰可见。
    • @SudiptaMaiti 当焦点在文本字段上时,您的警报会显示两次。这很有趣。你得到了什么结果?
    • 单击文本框 (IE10) 并按“alt”+P - 它显示“我是从按键按下!!!”但在 Chrome 中,它显示“我从按键按下!!!”,还显示“只有当焦点在文本框外时才应该调用我!!!”但我只期待一条消息,即“我是从按键开始!!!”。但是,如果用户单击文本框以外的任何其他位置并按“alt”+ P - 它应该是“只有当焦点在文本框之外时才应该调用我!!!”。
    • 澄清一下,即使这不是 OP 问题的原因,您是对的,标准做法是将事件作为参数接收。全局 event 变量不是标准的,在 Firefox 上不受支持。
    【解决方案5】:

    这只是一种解决方法,不能成为正确的解决方案 - accesskey 属性是问题所在,因此该属性将被暂时删除并再次添加它,正如@gfullam 所解释的那样。这可以通过使用 jquery 有效地完成,但不知道为什么 stopPropagation() 不起作用。

    <!DOCTYPE html>
    <html>
    <head>
    <title>Test</title>
    <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
    <script type="text/javascript">
        function keyDown(evt) {
            var e = evt || window.event;
            if (e.altKey && e.keyCode == 80) {
               //event.returnValue = false;
               //event.cancelBubble = true;
               //event.keyCode = 0;
                var allAccessKeys = $('[accesskey]').each(function () {
                    $(this).data('allAccessKeys', $(this).attr 
                    ('accesskey')).removeAttr('accesskey');
                });
    
                alert("I'm from key down!!!");
    
                setTimeout(function () {
                    allAccessKeys.each(function () {
                        $(this).attr('accesskey', $(this).data 
                        ('allAccessKeys'));
                    });
                }, 0);
            }
        }
    
        function clickMe() {
            alert("I should be called only when the focus is outside the textbox!!!");
        }
    </script>
    </head>
    <body>
    
    <div>
        <input type="text" onkeydown="keyDown(event);" />
        <input type="button" value="Click me" accesskey="p" onclick="clickMe();" />
    </div>
    
    </body>
    </html>
    

    【讨论】:

    • 我在我的原始答案中建议了一个 jQuery 解决方案(在我更新的答案中看到它)。我用纯 JS 进行了详细说明,因为您没有回应或对我的原始答案进行投票。您也没有在原始帖子中使用 jQuery。如果您要使用 jQuery,最好从 HTML 中删除事件处理程序属性并在脚本中动态绑定它们。
    猜你喜欢
    • 1970-01-01
    • 2017-11-30
    • 1970-01-01
    • 2010-10-25
    • 2017-09-09
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 2021-06-11
    相关资源
    最近更新 更多