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

时间:2011-12-20 17:44:48

标签: javascript html javascript-events

考虑以下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>

我想要的行为如下:

  • 当第一次点击aqua div区域的任何地方时(无论是否使用单选按钮),都应触发onfocus事件。
  • 当点击aqua div后,点击其他地方时,应该触发onb​​lur事件。
  • 每当有人多次点击aqua div区域内的任何地方时,即使从一个单选按钮点击另一个单选按钮,也不会触发任何事件。

它似乎在Chrome中运行良好,但不适用于FireFox 8或IE 9。

有关如何修复代码以使其正常工作的任何建议吗?

谢谢, 斯蒂芬

3 个答案:

答案 0 :(得分:3)

只有一些元素可以被聚焦,例如<a><input>。对于其他元素,您必须自己实现它。

// 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;
    };
})();

用法:

<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

答案 1 :(得分:0)

尽管我讨厌使用计时器,因为他们非常讨厌,这是我用计时器提出的解决方案。

http://jsfiddle.net/yV4uh/

这是如何工作的,它使用一个计时器来调用模糊事件并取消计时器,如果我对焦于我的一个单选按钮或包含单选按钮的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>

答案 2 :(得分:0)

另一种解决方案......

<!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>