Reputation: 304
I have a textarea field that I would like to have expand when it gains focus, and to contract back when it loses focus. Here is my test code:
$(function() {
var rows = parseInt(
$('textarea[name=details]').attr('rows')
);
$('textarea[name=details]').focus(function() {
$(this).animate({ rows: rows + 10 }, 250);
}).blur(function(e) {
$(this).animate({ rows: rows }, 250);
});
});
fiddle: http://jsfiddle.net/p0t1pzh7/3/
The problem is when the textarea has focus, and the user clicks on another input element. The textarea collapses back in this case, but the click event appears to be lost.
What I believe is happening, is the blur() handler is changing the page such that the target of the click changes position. Since blur runs before click, the click is in fact happening, but because the target has moved position, the click is no longer hitting an element. This is on Chrome 37.0.2062.120 under Linux.
The reason I think that, is if you comment out the resizing of the textarea in the blur handler, everything works as intended.
fiddle: http://jsfiddle.net/p0t1pzh7/4/
I've googled and searched SO for related issues and did find several discussions on the ordering of the blur and click events. Generally, solutions seemed to involve either adding a delay on the blur() action, or binding events on the other page element to keep track of what is occurring.
Both of those approaches seen rather fragile and error-prone. This behavior is really just a nicety, so if there's no "clean" way to do it, I'd rather just drop it altogether.
For reference, the delay approach does work, as can be seen in this fiddle: http://jsfiddle.net/p0t1pzh7/5/
And I understand why blur is triggered before click, but found it surprising that the target of the click event isn't "set" prior to blur being triggered. Is that expected behavior or does this vary between browsers?
Upvotes: 1
Views: 1259
Reputation: 505
Here is a solution that works without using any timeouts. See http://jsfiddle.net/29sw1abb/
HTML:
<textarea name="details" rows="5"></textarea>
<p>
<a href="#" class="button">click me</a>
</p>
JS
$(function() {
var target = undefined;
var rows = parseInt(
$('textarea[name=details]').attr('rows')
);
$('textarea[name=details]').focus(function() {
$(this).animate({ rows: rows + 10 }, 250);
}).blur(function(e) {
$(this).animate({ rows: rows }, 250);
});
$('.button').mousedown( function (e){
target = $(e.target).attr('class');
});
$(document).mouseup( function (e){
if (target) {
alert(target);
target = undefined;
}
});
});
I beleive you are correct in assuming the original click event is not firing because the target area moves out of position before the event can be evaluated. 'mousedown' seems to evaluate before the blur so we can capture the click target info with 'mousedown' and then action the event on 'mouseup'. Having larger targets, less dramatic animation and or slower animation also solves this issue.
Upvotes: 1
Reputation: 339
$(window).load(function () {
$('textarea.expand').focus(function () {
$(this).addClass("expanding")
$(this).animate({
height: "10em"
}, 500);
});
$('textarea.expand').blur(function () {
$(this).animate({
height: "1em"
}, 500);
// $(this).removeClass("expanding")
});
});
<table>
<tr>
<td>
<textarea class="expand" rows="1" cols="10"></textarea>
</td>
</tr>
<tr>
<td>Here is some text just below. Does it move?</td>
</tr>
</table>
.expand {
height: 1em;
line-height: 1em;
width: 300px;
padding: 3px;
}
.expanding {
position: absolute;
z-index: 9;
}
textarea {
resize: none;
}
Upvotes: 1
Reputation: 747
Introducing a delay works:
$(function() {
var rows = parseInt(
$('textarea[name=details]').attr('rows')
);
$('textarea[name=details]').focus(function() {
if (typeof(toDetails) != "undefined") { clearTimeout(toDetails); }
$(this).animate({ rows: rows + 10 }, 250);
}).blur(function(e) {
toDetails = setTimeout(function() { $(this).animate({ rows: rows }, 250); }.bind(this), 250);
});
});
Upvotes: 0
Reputation: 85643
You may use setTimeout function:
$('textarea[name=details]').focus(function() {
$(this).animate({ rows: rows + 10 }, 250);
}).blur(function(e) {
setTimeout(function(){
$(this).animate({ rows: rows }, 250);
}.bind(this),400);
Upvotes: 2