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?
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);
demo
Try this solution
$(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;
}
Here is a solution that works without using any timeouts. See http://jsfiddle.net/29sw1abb/
HTML:
<textarea name="details" rows="5"></textarea>
<p>
click me
</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.
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);
});
});
Related
I have an overlay div and a button, below, with a double click event attached. When the overlay is clicked, it is supposed to be hidden. If I double click the overlay, with the mouse over the button, the double click event is triggered in the button. Is this the correct behavior? Am i doing anything wrong? Is there any work around to prevent the triggering on the button?
Code example:
var $container = $('#absolute-container');
var $button = $('#button');
// On container click, hide the container
$container.on('click', function (e) {
e.stopPropagation();
e.preventDefault();
$container.hide();
});
$button.on('dblclick', function () {
alert('double click');
});
Here is a jsfiddle with an example:
https://jsfiddle.net/o0qsbd43/18/
Thanks
You'll need to have a very slight delay on firing off the normal click action, which you cancel when the double click event happens.
ref: https://css-tricks.com/snippets/javascript/bind-different-events-to-click-and-double-click/
var $container = $('#absolute-container');
var $button = $('#button');
function doClickAction(e) {
e.stopPropagation();
e.preventDefault();
$container.hide();
}
function doDoubleClickAction(e) {
e.stopPropagation();
e.preventDefault();
}
var timer = 0;
var delay = 200;
var prevent = false;
$container
.on("click", function(e) {
timer = setTimeout(function() {
if (!prevent) {
doClickAction(e);
}
prevent = false;
}, delay);
})
.on("dblclick", function(e) {
clearTimeout(timer);
prevent = true;
doDoubleClickAction(e);
});
$button.on('dblclick', function () {
alert('double click');
});
.absolute-container {
background: black;
opacity: 0.5;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="absolute-container" class="absolute-container" ></div>
<div>
<button id="button">Double Click Button</button>
</div>
I think on click and double click events event is fired when you release the bouse button. Double click is a single event that calculates time between clicks.
In your case you release button on single click but for firing double click browser is still waiting if there will be second click and release of the button within a second or so depending on OS configuration.
So in your case second click release occurs on top of the button and double click event is fired.
Solution I could propose is to make a delay when hiding your overlay layer. For example if you will make 1 second timeout and during this timeout make a fadeout effect or something like this, the time interval the browser is waiting for the second click to fire double click even will expire and you can do your double click on the button. Note sure I explained correctly and intuitively.
here is my fiddle
the '.item' click function that shows the '.pull_down_content' doesn't always work why is this?
i've found that if you click the first 'tile' this will work fine and whilst that is open click the next tile still works fine but if you then go back to the original tile the click function stops working and only the hover works?
why is this?
here is part of my code..
$(this).children('.item').on('mouseenter mouseleave click', function(e) { e.stopPropagation();
if ($('.timelineTile').hasClass("clicked")) {
if (!$(this).data('clicked')) {
var Height = e.type==='mouseenter' ? '60px' : e.type==='click' ? '300px' : '0px';
$(this).siblings('.pull_down_content').stop().animate({'height': Height}, 300);
$(this).siblings('.pull_down_content').children('.inner').css({'display': 'block'}, 300);
if (e.type==='click') $(this).data('clicked', true);
}else{
if (e.type==='click') {
$(this).data('clicked', false);
$(this).siblings('.pull_down_content').stop().animate({'height': '0px'}, 300);
$(this).siblings('.pull_down_content').children('.inner').css({'display': 'none'}, 300);
}
} }
});
It looks like you are adding additional click events on the children every time you click on '.timelineTile'. You should move the above code outside of the '.timelineTile' click event. At the very least remove the existing events before re-adding them.
I have an element, which fills the screen. Under that I have another element which does NOT fill the screen but is hidden. So you can't manually scroll to that.
The first element which is filling the screen has the following CSS properties:
header {
position: relative;
height: 100vh;
background-color: green;
}
And the second element has these CSS properties:
#content {
display: none;
position: relative;
height: 1500px;
width: 100%;
background-color: yellow;
}
I use this code to trace the scrolling:
$('header').bind("DOMMouseScroll mousewheel", function(e) {
...
});
Inside this method I check which panel is activated (by a self created boolean) and which direction I'm scrolling to, by doing this:
$('header').bind("DOMMouseScroll mousewheel", function(e) {
var wheelDelta = e.originalEvent.wheelDelta;
if (active === header && wheelDelta <= 0) {
...
}
});
Inside that if statement I give call a method that displays the #content element below it and smoothly scrolls to it, and when its done scrolling it hides the element where we have scrolled from (header). I'm using this piece of code for that:
$('body').bind("DOMMouseScroll mousewheel", function (e) {
event.preventDefault();
var wheelDelta = e.originalEvent.wheelDelta;
$('header').unbind("DOMMouseScroll mousewheel");
if (active === header && wheelDelta <= 0) {
showScrollHide(500, content, 1000, header, 250, function () {
_window.scrollTop(0);
_scrollBackBtn.fadeIn();
active = content;
});
}
});
This works perfectly, whenever I scroll down on the header element while its active. It smoothly scrolls down to the #content element under it.
There I have a button which scrolls back up the page, I have this code for it:
_scrollBackBtn.on('click', function() {
if (active === content) {
active = header;
scrollBackHide(header, content, 500, 250, function() {
window.location = '#';
});
}
});
Which also works perfectly, it scrolls back to the top of the page.
But whenever I try to scroll down again, it doesn't do anything. How does this come?
I had to add $('header').unbind("DOMMouseScroll mousewheel"); to the code, otherwise it looked very ugly in the end result.
Whenever I added $('header').stop(); in the same section, it didn't make any difference.
So my question is. How can this .bind method be used again for the same thing?
Here's a demo if you don't understand what I mean. Whenever you scroll down on the header element, and scroll back up via the button. Its not doing the same again.
You are unbinding your mousewheel handlers when they run once and then never re-binding them, so there are no event handlers in place to react to mousewheel events after you click the button. Re-binding that event handler when you click the button does the trick.
Here is a fiddle that does that and should point you in the right direction.
I pulled your mousewheel handler out into a function so we can reuse it whenever we need to bind those events:
var handleMouseWheel = function(e){
e.preventDefault();
var wheelDelta = e.originalEvent.wheelDelta;
if (active === header && wheelDelta <= 0) {
$('body').unbind("DOMMouseScroll mousewheel");
showScrollHide(500, content, 1000, header, 250, function () {
_window.scrollTop(0);
_scrollBackBtn.fadeIn();
active = content;
});
}
};
and use that to reattach an event handler when the button is clicked:
_scrollBackBtn.on('click', function () {
if (active === content) {
scrollBackHide(header, content, 500, 250, function () {
window.location = '#';
active = header;
});
$('body').bind("DOMMouseScroll mousewheel", handleMouseWheel);
}
});
I have a problem with the animation. Here is my code:
<html>
<head>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.9.1.js'></script>
<title>title</title>
<style>
div{
width: 100px;
height: 100px;
background-color: green;
position:relative;
left: 0px;
}
</style>
</head>
<body>
<h1>Animation</h1>
<select id='text_1' type="text">
<option>100</option>
<option>200</option>
<option>300</option>
<option>400</option>
<option>500</option>
<option>600</option>
<option>700</option>
<option>800</option>
<option>900</option>
<option>1000</option>
</select>
<button>Animate Div</button><br/><br/>
<div></div>
<script>
$(document).ready(function(){
var number = 500;
$('#text_1').bind('change', function(){
var number_2 = $('#text_1').val() != '' ? $('#text_1').val() : 500;
animation(number_2);
});
animation(number);
});
function animation(num){
$('button').click(function(){
$('div').animate({
'left': num+'px',
}, 1000).animate({
'left': '0px',
}, 1000);;
});
}
</script>
</body>
</html>
When I change select option (example: 300) and click the button, then does a two animations.
when I change select option again, then does a tree animations.
What is wrong. Please help.
This isn't the most elegant solution, but if you modify your code to look like this:
function animation(num) {
$('button').unbind('click'); //this removes the old click event
$('button').click(function(){
....
}
}
then that should solve your problem.
Try
$(document).ready(function()
{
$('button').on('click',function()
{
var num = $('#text_1').val() != '' ? $('#text_1').val() : 500;
$('div').animate(
{
'left': num+'px',
}, 1000).animate(
{
'left': '0px',
}, 1000);
});
});
It might not matter for this, but you should learn how to use a Javascript debugger, like Firebug, if that might give you a clue.
If you used a debugger, and you set a breakpoint inside the "click" function, you'd see it getting hit multiple times, adding up to the number of times you've changed the option dropdown. I'm guessing that's because your "animation()" function is binding a NEW function to the "click" event every time it's called, and not removing the previous ones, so clicking the button will execute the "click" function multiple times.
This is what you probably meant
$(document).ready(function(){
var number = 500;
$('button').click(function(){
$('div').stop().animate({
'left': number+'px',
}, 1000).animate({
'left': '0px',
}, 1000);
});
$('#text_1').bind('change', function(){
number = $('#text_1').val() != '' ? $('#text_1').val() : 500;
});
});
otherwise you are binding the click event to the button every single time you animate which becomes the problem. The first time the click is binded once, so it animates once, then you animation binds to click again, so it animates twice. Also notice the stop() which causes the div to stop what it is doing and start a new animation to prevent stuff where you click the button multiple times and it keeps animating for several seconds.
You're binding the click event multiple times.
Change it to this:
$('button').off('click').on('click', function(){
$('div').animate({
'left': num+'px',
}, 1000).animate({
'left': '0px',
}, 1000);;
});
Keep in mind .off and .on replaced .bind and .unbind
Also, it's better for performance reasons to chain functions like this.
I have this code which animates between divs sliding out. If an item is clicked, it's relevant content slides out. If another item is clicked, the current content slides back in and the new content slides out.
However,
var lastClicked = null;
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).toggle(function() {
if (lastClicked && lastClicked != this) {
// animate it back
$(lastClicked).trigger('click');
}
lastClicked = this;
$('.each-brew-content.'+animCls).show().animate({ left: '0' }, 1000).css('position','inherit');
}, function() {
$('.each-brew-content.'+animCls)
.animate({ left: '-33.3333%' }, 1000, function() { $(this).hide()}) // hide the element in the animation on-complete callback
.css('position','relative');
});
})(animateClasses[i]); // self calling anonymous function
}
However, the content sliding out once the already open content slides back is sliding out too quickly - it needs to wait until the content has fully slided back in before it slides out. Is this possible?
Here's a link to what I'm currently working on to get an idea (http://goo.gl/s8Tl6).
Cheers in advance,
R
Here's my take on it as a drop-in replacement with no markup changes. You want one of three things to happen when a menu item is clicked:
if the clicked item is currently showing, hide it
if something else is showing, hide it, then show the current item's content
if nothing is showing, show the current item's content
var lastClicked = null;
// here lastClicked points to the currently visible content
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).click(function(event){
if(lastClicked && lastClicked == animCls){
// if the lastClicked is `this` then just hide the content
$('.each-brew-content.'+animCls).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
}).css('position','relative');
lastClicked = null;
}else{
if(lastClicked){
// if something else is lastClicked, hide it,
//then trigger a click on the new target
$('.each-brew-content.'+lastClicked).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
$(event.target).trigger('click');
}).css('position','relative');
lastClicked = null;
}else{
// if there is no currently visible div,
// show our content
$('.each-brew-content.'+animCls).show()
.animate({ left: '0' }, 1000)
.css('position','relative');
lastClicked = animCls;
}
}
});
})(animateClasses[i]); // self calling anonymous function
}
Well, I'm pretty sure there are other more easy possibilities and I didn't have much time but here is a working jsfiddle: http://jsfiddle.net/uaNKz/
Basicly you use the callback function to wait until the animation is complete. In this special case it's the complete: function(){...}
$("document").ready(function(){
$('#ale').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
$('#bramling').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
});
I give a toggled class if a div is expanded. Since the animation on your page seems to be pretty much broken I think this would be a better way to do this. But remember: my code isn't really good. Just fast and it can be refactored. It's working tho..
Rather than using toggles, bind an on "click" handler to your ".each-brew" divs. In the handler, first hide content divs and then show the appropriate content when that animation completes. You can do that with either a promise or a callback. Something like this...
$(".each-brew").on("click", function (event) {
$(".each-brew-content").show().animate({ left: "0" }, 1000, function() {
// Get the brew name from the class list.
// This assumes that the brew is the second class in the list, as in your markup.
var brew = event.currentTarget.className.split(/\s+/)[1];
$(".each-brew-content." + brew).animate({ left: "-33.3333%" }, 1000, function() { $(this).hide(); });
});
});
I think an event and observer would do the trick for you.
set up the callback function on completion of your animation to fire an event.
the listener would first listen for any animation event and after that event is triggered listen for the completion event. when the completion event is fired execute the initial animation event.run method (or whatever you would want to call it)
Within the listener
on newanimationeventtriger(new_anim) wait for x seconds (to eliminate infinite loop poss) while if this lastevent triggers done == true{
new_anim.run();
}