admin管理员组文章数量:1026989
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
3 Answers
Reset to default 4Only 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>
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
3 Answers
Reset to default 4Only 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>
版权声明:本文标题:javascript - How can the onfocus event on a group of radio buttons act like a single control? - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745669554a2162379.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论