最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can the onfocus event on a group of radio buttons act like a single control? - Stack Overflow

programmeradmin1浏览0评论

Consider the following HTML and JavaScript, which is also here: /

<!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.
  • When, after having clicked in the aqua div, one clicks elsewhere, the onblur event should get triggered.
  • 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.

It seems to work fine in Chrome, but not FireFox 8 or IE 9.

Any suggestions as to how I can fix my code to get it to work?

Consider the following HTML and JavaScript, which is also here: http://jsfiddle/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.
  • When, after having clicked in the aqua div, one clicks elsewhere, the onblur event should get triggered.
  • 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.

It seems to work fine in Chrome, but not FireFox 8 or IE 9.

Any suggestions as to how I can fix my code to get it to work?

Share Improve this question edited Sep 1, 2022 at 16:39 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Dec 20, 2011 at 17:44 Stephen OberauerStephen Oberauer 5,4058 gold badges57 silver badges78 bronze badges 4
  • 1 Your code seems to work in Chrome 15, but not FF 7. Clicking radio button in FF7 triggers the blur event and you don't want it to. – mrtsherman Commented Dec 20, 2011 at 17:48
  • ah... yes it seems to work in Chrome. Doesn't work in IE 9 though :( – Stephen Oberauer Commented Dec 20, 2011 at 17:51
  • Why don't you use a jQuery or any other framework to make browser patible, and use the bind features, that will work on all browsers? – Gabriel Gartz Commented Dec 20, 2011 at 18:03
  • Would love to use jQuery, but this is the first time I'm working on this rather massive project, and for some reason the people who have been working on the code since it was born and know what they're doing have decided not to. My tiny change is probably not a good enough reason to change that. – Stephen Oberauer Commented Dec 21, 2011 at 9:58
Add a ment  | 

3 Answers 3

Reset to default 4

Only some elements can be focused, e.g. <a> and <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

As much as I hate using timers, because they're very hacky, here's a solution I came up with, using a timer.

http://jsfiddle/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.

<!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/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>
发布评论

评论列表(0)

  1. 暂无评论