Reputation: 16109
There are two input
elements which hide themselves on blur. The problem is that the click event which caused the blur is lost if the click was on an element which comes after the element being hidden in the DOM.
If the clicked element is before the blurred element, it works as expected
This simple snippet demonstrates:
<html>
<script src="./jquery.min.js" ></script>
<script type="text/javascript">
$(document).ready(function() {
$('input').blur(function(e) {
console.log('blurred');
$(this).hide();
});
$('.a').click(function() {
console.log('div clicked');
});
$(document).click(function() {
console.log('doc clicked');
});
});
</script>
<body>
<div id="div1">
<div class="a"><input /></div>
<div class="a"><input /></div>
</div>
</body>
</html>
If the bottom input is clicked, then the top, it works as expected - "blurred" prints, then "div clicked", then "doc clicked" and the bottom input is hidden.
If the top input is clicked, then the bottom, the top input is hidden, only "blurred" prints and the click event can't be handled anywhere.
If the call to hide
is commented out, it also works as expected.
Using .live
doesn't make a difference.
I can think of ways to work around this, but none are very good. Any thoughts on why the order of the input
s matters, or better solutions?
Thanks.
Upvotes: 3
Views: 2845
Reputation: 42277
OK OK OK this is just us collectively being stupid together.
Look what is happening when you are clicking from top to bottom.
Top has focus. When you click into the 2nd input THAT IS UNDERNEATH IT, the blur event fires before the click event registers on the 2nd div.
What's happening is that you are clicking into the 2nd input to give it focus. Since events bubble up from the target that's clicked (the 2nd input), it gets focus. But since the 1st input blur event fires first, it reduces the height of the top div to 0 and that immediately moves the 2nd div up beyond the range of the the current mouse position. Therefore, the click event on the 2nd div never happens, because the div is already moved out of the way before that happens.
You can see why in this jsfiddle. http://jsfiddle.net/Z884F/ I've added
elements to maintain the div heights even after their inputs are being hidden. If you look at the console, you'll see it works as expected.
The reason Firefox works is that it handles the timing of events a little differently.
IGNORE MY ORIGINAL ANSWER BELOW:
This is very strange behavior. I've verified your results in Chrome and Safari on Mac OS 10.7. However, Firefox 12.0 works as expected.
If you do the blur inside a 500ms setTimeout, it works as expected. Though I think the event on the parent div is still being removed.
I have a jsfiddle here, http://jsfiddle.net/gsuTw/
I would highly recommend filing this as a bug over at the jQuery dev site. This looks like a bug with blur().
Upvotes: 2
Reputation: 9528
I don't think this gives an answer to why the event got swallowed. But if the position of the input is not a concern, you can change the visibility instead.
$('input').blur(function(e) {
console.log('blurred');
$(this).css('visibility', 'hidden');
});
Upvotes: 0
Reputation: 147453
The srcElement property of event objects is peculiar to the IE event model. jQuery "normalises" the event object by adding e.target
where absent but not e.srcElement
:
// Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
if ( !event.target ) {
event.target = originalEvent.srcElement || document;
}
So in the line:
> $(e.srcElement).hide();
'e.srcElement` returns undefined and the statement does nothing.
The fix is to either change e.srcElement
to e.target
or use this
instead.
Note that in the listener, this
is the element that called the listener, whereas target
is the element from which the original event was dispatched. Therefore they may or may not reference the same element.
In the blur listeners, this === event.target
, but not in the click listeners.
BTW, it's considered bad practice to chain calls together because it makes errors (like this one) hard to find and debug. Having a library that silently ignores such errors makes it even harder, consider instead:
$('input').blur(function(e) {
if (e.srcElement) {
$(e.srcElement).hide();
} else {
// No e.srcElement? what now!!
}
});
Defensive coding practices are passe for web developers, but they are far more reliable and easier to debug.
Upvotes: 0