简体   繁体   English

一组单选按钮上的onfocus事件如何像单个控件一样?

[英]How can the onfocus event on a group of radio buttons act like a single control?

Consider the following HTML & JavaScript, which is also here: http://jsfiddle.net/5CetH/ 考虑以下HTML和JavaScript,它也在这里: http//jsfiddle.net/5CetH/

<!DOCTYPE html>
<html>
<head>
    <title>Untitled Page</title>
<script type="text/javascript">
  var i=0;

  function _focus() {
    var message = document.getElementById("message");
    message.value = message.value + ++i + ". Focus\r\n";
  }

  function _blur() {
    var message = document.getElementById("message");
    message.value = message.value + ++i + ". Blur\r\n";
  }

</script>
</head>
<body>
<div style="background-color: Aqua; width: 100px; height: 50px" onfocus="_focus()" onblur="_blur()" tabindex="0">
  <input name="rb" type="radio" /><br />
  <input name="rb" type="radio" />
</div>
<br />
<textarea id="message" rows="15" cols="50"></textarea>
</body>
</html>

The behaviour that I want is as follows: 我想要的行为如下:

  • When one clicks anywhere in the aqua div area for the first time (whether on a radio button or not), the onfocus event should get triggered. 当第一次单击aqua div区域中的任何位置时(无论是否使用单选按钮),都应触发onfocus事件。
  • When, after having clicked in the aqua div, one clicks elsewhere, the onblur event should get triggered. 当点击aqua div后,点击其他地方时,应该触发onb​​lur事件。
  • Whenever one clicks anywhere within the aqua div area more than once, no events should fire, even if clicking from one radio button to the other. 每当有人多次点击aqua div区域内的任何地方时,即使从一个单选按钮点击另一个单选按钮,也不会触发任何事件。

It seems to work fine in Chrome, but not FireFox 8 or IE 9. 它似乎在Chrome中运行良好,但不是FireFox 8或IE 9。

Any suggestions as to how I can fix my code to get it to work? 有关如何修复我的代码以使其工作的任何建议?

Thanks, Stephen 谢谢,斯蒂芬

Only some elements can be focused, eg <a> and <input> . 只能聚焦一些元素,例如<a><input> For other elements you have to implement it yourself. 对于其他元素,您必须自己实现它。

// window.addFocusToElem(elem, callbacks) - returns id for removeFocusFromElem
// window.removeFocusByID(id)
// in IE <= 8 the blur events get fired in the wrong order!
// callbacks: { onFocus: function(e) {}, onBlur: function(e) {} } - both methods are optional
(function() {
    var addEvent, removeEvent;
    (function() {
        // sometimes in IE <= 8 the window.blur event isn't fired when the
        // window loses the focus but instead it is fired when the window gets
        // the focus back again. This requires some hacking - and because
        // 'fireEvent' in window === false it even requires some more hacking.
        var hasFocus = true;
        var queue = [];

        addEvent = function(node, evtType, callback) {
            if('addEventListener' in node)
                node.addEventListener(evtType, callback, false);
            else { // IE <= 8
                if(evtType === 'blur') {
                    queue.push(callback);
                }
                node.attachEvent('on' + evtType, callback);
            }
        }

        removeEvent = function(node, evtType, callback) {
            if('removeEventListener' in node)
                node.removeEventListener(evtType, callback, false);
            else { // IE <= 8
                if(evtType === 'blur') {
                    var length = queue.length;
                    while(length--) {
                        if(callback === queue[ length ]) {
                            queue.splice(length, 1);
                            break;
                        }
                    }
                }
                node.detachEvent('on' + evtType, callback);
            }
        }

        // IE <= 8
        if('documentMode' in document && document.documentMode <= 8) {
            setInterval(function() {
                if(!document.hasFocus() && hasFocus) {
                    hasFocus = false;
                    for(var o in queue) {
                        queue[ o ](document.createEventObject());
                    }
                }
            }, 100);
            addEvent(window, 'focus', function(e) {hasFocus = true;});
        }
    })();

    function doClick(node, evtType) {
        if('click' in node) { // most Browser (HTML-DOM)
            node.click();
        } else if('createEvent' in document) { // at least Chrome (16)
            var e = document.createEvent('MouseEvents');
            e.initEvent('click', true, true);
            node.dispatchEvent(e);
        } else {

        }
    }

    var id = 0;
    var queue = [];

    window.addFocusToElem = function addFocusToElem(elem, callbacks) {
        var _id = id++;
        var entry = queue[ _id ] = {
            elem: elem,
            onFocus: function(e) {
                removeEvent(entry.elem, 'click', entry.onFocus);
                addEvent(document, 'click', entry.onBlur);
                if('onFocus' in callbacks &&
                   typeof callbacks.onFocus === 'function') {
                    callbacks.onFocus(e);
                }
            },
            onBlur: function(e) {
                var node = 'target' in e ? e.target : e.srcElement;
                while(node) {
                    if(node === entry.elem) {
                        break;
                    }
                    node = node.parentNode;
                }
                if(!node) {
                    removeEvent(document, 'click', entry.onBlur);
                    addEvent(area, 'click', entry.onFocus);
                    if('onBlur' in callbacks &&
                       typeof callbacks.onBlur === 'function') {
                        callbacks.onBlur(e);
                    }
                }
            }
        };
        addEvent(elem, 'click', entry.onFocus);
        addEvent(window, 'blur', function(e) {
            doClick(elem.parentNode);
        });
        addEvent(document, 'keyup', function(e) {
            if(e.keyCode === 9) { // tab
                var node = 'target' in e ? e.target : e.srcElement;
                while(node) {
                    if(node === elem) {
                        doClick(elem);
                        break;
                    }
                    node = node.parentNode;
                }
                if(!node) {
                    doClick(elem.parentNode);
                }
            }
        });
        return _id;
    };
    window.removeFocusByID = function removeFocusByID(id) {
        if(id in queue) {
            var entry = queue[ id ];
            removeEvent(entry.elem, 'click', entry.onFocus);
            removeEvent(document, 'click', entry.onBlur);
            delete queue[ id ];
            return true;
        }
        return false;
    };
})();

Usage: 用法:

<div style="background-color: Aqua; width: 100px; height: 50px" id='area'>
    <input name="rb" type="radio">Foo<br>
    <input name="rb" type="radio">Bar
</div>
<script type='text/javascript'>
var id = addFocusToElem(document.getElementById('area'), {
    onFocus: function(e) {
        // statements
    },
    onBlur: function(e) {
        // statements
    }
});
// removeFocusByID(id);
</script>

jsFiddle 的jsfiddle

As much as I hate using timers, because they're very hacky, here's a solution I came up with, using a timer. 尽管我讨厌使用计时器,因为它们非常hacky,这是我提出的解决方案,使用计时器。

http://jsfiddle.net/yV4uh/ http://jsfiddle.net/yV4uh/

How this works is that it uses a timer to call the blur event and cancels the timer if I focus back on one of my radio buttons or the div containing the radio buttons. 这是如何工作的,它使用一个计时器来调用模糊事件,并取消定时器,如果我专注于我的一个单选按钮或包含单选按钮的div。

<!DOCTYPE html>
<html>
<head>
    <title>Untitled Page</title>
<script type="text/javascript">
  var i = 0;

  var focused = false;

  var blurTimer = null;

  function startBlurTimer() {
    blurTimer = window.setTimeout("blurTimerFinished()", 1);
  }

  function cancelBlurTimer() {
    if (blurTimer != null) {
      clearTimeout(blurTimer);
      blurTimer = null;
    }
  }

  function blurTimerFinished() {
    cancelBlurTimer();
    focused = false;
    var message = document.getElementById("message");
    message.value = message.value + ++i + ". Blur\r\n";
  }

  function _focus() {
    if (blurTimer == null) {
      focused = true;
      var message = document.getElementById("message");
      message.value = message.value + ++i + ". Focus\r\n";
    }
    else
      cancelBlurTimer();
  }

  function _blur() {
    if (focused) {
      startBlurTimer();
    }
  }  
</script>
</head>
<body>
<div style="background-color: Aqua; width: 100px; height: 50px" onfocus="_focus()" onblur="_blur()" tabindex="0">
  <input onfocus="_focus()" onblur="_blur()" name="rb" type="radio" /><br />
  <input onfocus="_focus()" onblur="_blur()" name="rb" type="radio" />
</div>
<br />
<textarea id="message" rows="15" cols="50"></textarea>
</body>
</html>

An alternative solution... 替代解决方案......

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <title></title>
</head>
<body onload="test()">
    <input>
    <div style="background-color: Aqua; width: 100px; height: 50px" id='area'>
        <input id="rb1" name="rb" type="radio">Foo<br>
        <input id="rb2" name="rb" type="radio">Bar
    </div>
    <input>
    <pre id='dump'></pre>
    <script type='text/javascript'>
'use strict';
function dump(data) {
    document.getElementById('dump').appendChild(document.createTextNode(data + '\n'));
}

function addEvent(node, evtType, callback)
{
    if('addEventListener' in node)
        node.addEventListener(evtType, callback, false);
}

function removeEvent(node, evtType, callback)
{
    if('removeEventListener' in node)
        node.removeEventListener(evtType, callback, false);
}    

function addHandler(element, focus, blur)
{
  var event = {
    focused: false,
    element: element,
    focus: focus,
    blur: blur,
    onClick: function(e) {
      event.targetNode(e).focus();  // Because Chrome doesn't focus when clicked
    },
    onFocus: function(e) {
      if (event.focused)
        return;

      event.focused = true;

      addEvent(document, 'click', event.onBlur);
      addEvent(document, 'keyup', event.onBlurIfTab);

      focus();
    },
    onBlur: function(e) {
      if (!event.focused)
        return;
      if (event.thisElement(event.targetNode(e)))
        return;

      event.focused = false;
      removeEvent(document, 'click', event.onBlur);
      removeEvent(document, 'keyup', event.onBlurIfTab);

      blur();
    },
    onBlurIfTab: function(e) {
      if (e.keyCode === 9) { event.onBlur(e) }
    },
    targetNode: function(e) {
      return 'target' in e ? e.target : e.srcElement;
    },
    thisElement: function(node) {
      // Test to see if we're on the element node
      while (node) {
        if (node == event.element) {
          return true;
        }
        node = node.parentNode;
      }
      return false;
    },
    findButtons: function() {
      var buttons = [];
      event.innerButtons(event.element, buttons);
      return buttons;
    },
    innerButtons: function(node, results) {
      if (node.nodeName == "INPUT") {
        results.push(node);
      }
      else
        if (node.childNodes) {
        var children = node.childNodes;
        for (var i in children)
          event.innerButtons(children[i], results);
      }
    }
  };
  var buttons = event.findButtons();
  for (var i = 0; i < buttons.length; i++) {
    addEvent(buttons[i], 'focus', event.onFocus);
    addEvent(buttons[i], 'click', event.onClick);
  }
}

function focus()
{
    dump('focus');
}

function blur()
{
    dump('blur');       
}

function test()
{
    var area = document.getElementById('area');
    addHandler(area, focus, blur);
}
    </script>
</body>
</html>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM