jQuery assign event handler with timeouts per element - javascript

Interesting one for me. I've got a video player whos controls are showed on hover. Initially, I did this with CSS but had to change strategy to javascript to play nice with browsers fullscreen api (which, incidentally means you're always hovering on the video).
My new approach is setting an event handler for mousemove for the video's container that adds a class and sets a timeout to remove it, and if the timeout is already set, clears it. This works perfectly, but the logic doesn't scale to more than one player.
TLDR: how can I expand my logic to scale to more than one video container? The scope of the time variable needs to be contained to each elements event, but also outside of the handler so that it can be cleared out from one event to the next (on the same element).
Thanks for your help- these logic questions are always difficult for me.
Here's a jsFiddle example. You'll notice that it works well when you limit hovering to one element, but there are issues when you expand to the rest of the elements on the page.
HTML:
<div class="cont">
<div class="controls">controls</div>
</div>
JavaScript:
var time;
$('body').on('mousemove', '.cont', function(){
var thiis = $(this);
if (time){
clearTimeout(time);
}
thiis.addClass('showControls');
time = setTimeout(function(){
thiis.removeClass('showControls');
}, 2000);
});

You could store the time variable using jQuery's data method which can store data on each of your elements.
$('body').on('mousemove', '.cont', function(){
var thiis = $(this),
// get the time from the data method
time = thiis.data("timer-id");
if (time){
clearTimeout(time);
}
thiis.addClass('showControls');
var new_time = setTimeout(function(){
thiis.removeClass('showControls');
}, 2000);
// save the new time
thiis.data("timer-id", new_time);
});

Related

jQuery get the tager from event

I am using Hexagon.js and I need to get the .html() of the caller. If i do something like event.target and start moving with the slider, as long as the cursor is on the slider it works just fine, but if i move the cursor somewhere else (and still holding the slider, just like any other, dragging it with cursor and moving outisde of the slider box), I will get the data of that element I am currently hovering above. I need just the data of the caller, not form enyone else.
$(".slider").each(function() {
slider = new hx.Slider(this, {max:100});
slider.on('change', function(value){
var target = $( event.target );
console.log(target);
});
});
Thanks for any hep.
Without seeing the html it's a little bit hard to answer but well, according to docs you have to initialize the slider with an html element and, you do.
new hx.Slider(this, {max:100});
this in this line is the html element, so you could just use it.
Now the problem is that since you use function () {...} syntax, each function has its own this.
You may either use arrow function (they don't have their own this) as .on(...) callback (if it works ok, because sometimes it doesn't with JQuery) or save current this in a variable.
$(".slider").each(function(){
const slide = this;
const slider = new hx.Slider(
slide,
{max:100}
);
slider.on('change', function(value){
console.log(slide, `is html element!`);
console.log(value, `is the value that has just changed!`);
});
});

Multiple simultaneous button animation

I am trying to create an animation for a button on click event. A simple function which simply consists on toggling classes and setting timeouts is used for the animation.
It works well for one button but when I have more than one button and I click two or more of them consecutively before the animation is finished, the animation stops and continues on the element which has been clicked later.
So the problem is to make the animation function to refer to the object which has triggered it, therefore creating multiple instances of it, for which I haven't been able to find a simple solution after hours of search).
Thanks in advance.
There's a simplified example (real example has more classes toggles):
$('.myButton').on('click', animateButton);
function animateButton(){
var $this = $(this);
$this.addClass('animate');
setTimeout(function(){
$this.removeClass('animate');
},2000)
}
EDIT: I've made a fiddle: https://jsfiddle.net/8ozu14am/
EDIT2: SOLVED
Using $.proxy() it is possible to maintain the context.
$('.myButton').on('click', animateButton);
function animateButton(){
$(this).addClass('animate');
setTimeout($.proxy(function(){
$(this).removeClass('animate');
},this),2000)
}
Jquery return an Event to your handler.
It have the property targetwhich is the DOM element that initiated the event.
$('.myButton').on('click', animateButton);
function animateButton(evt){
var $this = $(evt.target);
$this.addClass('animate');
setTimeout(function(){
$this.removeClass('animate');
},2000)
}
SOLVED
Using $.proxy() it is possible to maintain the context.
https://api.jquery.com/jQuery.proxy/
$('.myButton').on('click', animateButton);
function animateButton(){
$(this).addClass('animate');
setTimeout($.proxy(function(){
$(this).removeClass('animate');
},this),2000)
}

jquery change value of input created after page load

I simply need to change the value of the input field which is created on button click. I have tried the below but it gives me undefined error since the element was not there when the page loaded. any ideas?
$('#test').val("test");
I use the below code for click event but I have no idea how to do the same thing for the above one too
$('body').on("click", ".btnx", function() {
//do something
});
$('#test').click(function(){
//code here
});
$('#test').empty();
$('#test').append('test');
There isn't anything like delegated events when you want to set the value of an input that is added out of your control, because there is no event to wait for.
You can wait for the element to come into existance, and set the value when it's there:
var handle = window.setInterval(function(){
var i = $('#test');
if (i.length) {
i.val('test');
window.clearInterval(handle);
}
}, 100);
The interval 100 means that it checks ten times per second, so it may be as long as 1/10th of a second after the element is created that the value is changed. You can adjust the value depending on how fast you need the value to be set, and how much overhead you can allow.

Using a function with an interval to change a class of a div?

I'm trying to create a simple game that has a function that selects a random div(out of a selection)and then sets a random countdown using an interval and when the countdown hits 0 the class of that div will change.
Then i have it so when you click on something assigned with that class it will change back to the original class.
At the moment when i'm running my code the divs seem to be changing after the countdown but won't change when i click them.
But my main problem is that the main function that changes the random divs is only running once.
The divs ("box") start as .wait
My code:
var react = function(){
var box = parseInt(Math.random()*64);
while($("box"+box).hasClass("now")) {
box = parseInt(Math.random()*64);
}
var timer = parseInt((Math.random()*10)+2);
var countdown=setInterval(function(){
timer-=1
$("#box"+box).text(parseFloat(timer.toFixed(0)));
if(timer<=0){
clearInterval(countdown)
$("#box"+box).text("");
$("#box"+box).text("");
$("#box"+box).removeClass("wait");
$("#box"+box).addClass("now");
}
},1000)
}
$(document).ready(function(){
//paint\\
//$(".wait").click(function() {
//$(this).toggleClass("now")
//})
//paint\\
setInterval(react(),1000);
$(".now").click(function(){
$(this).removeClass("now");
$(this).addClass("wait");
})
})
The issue is how you're binding your click event. You'll want to delegate that event, rather than use click().
When setInterval runs, it adds a class, 'new' to an element. However, since no elements had that class name (when calling click() in $doc.ready), no handler is triggered.
First, a fiddle demonstrating this works: http://jsfiddle.net/yvvMp/
Here's an example using your code + delegating the events:
var react = function(){
var box = parseInt(Math.random()*64);
while($("#box"+box).hasClass("now")) {
box = parseInt(Math.random()*64);
}
var timer = parseInt((Math.random()*10)+2);
var countdown=setInterval(function(){
var $el = $('#box' + box);
timer-=1
$el.text(parseFloat(timer.toFixed(0)));
if(timer<=0){
clearInterval(countdown);
$el.text("")
.removeClass("wait")
.addClass("now");
}
},1000);
}
$(document).ready(function(){
$parent = $('.parent-to-now-elements') // $('body') works, but not as efficient
setInterval(react, 1000);
$parent.on('click', '.now', function(){
$(this).removeClass("now");
$(this).addClass("wait");
})
})
Tilwin's answer will work, but you run into the chance that the same element could have multiple event handlers bound. Depending on how long the game runs, and how often a user gets the same DIV element randomly selected, your DOM could look something like:
<div class='wait wait wait wait wait wait wait wait wait'></div>
Worse, each time jQuery calls the click handler, you're forcing the browser to touch the DOM (depending on the game, this could be bad!)
Here's an example: http://jsfiddle.net/pjMcv/
(When a block turns green, click it. Then wait for it to turn red again and click...)
(Tilwin has edited his answer, removing .click out of setInterval. His edited answer is better, but it still has a downside - it requires n number of event bindings. Works for simple games, but if your game has 1000 squares, you'll have 1000 event handlers)
"But my main problem is that the main function that changes the random divs is only running once."
i'm assuming that you're referring to the following line.
setInterval(react(), 1000);
modify it as follows:
setInterval(react, 1000);
update:
assuming you've a fixed number of div, you can assign a common handler for all of them at page load like
$(document).ready(function(){
$('your-common-div-selector').click(function(){
if($(this).hasClass("now")&& !$(this).hasClass("wait")) {
this.removeClass("now");
this.addClass("wait");
});
});

Checking for visibility of a toggled element

I have a button which toggles the visibility of a <div> below it and want to modify the text on the button depending on the visibility of said <div>.
Here is a live demo on jsFiddle
If you click on "Saved Data", the first time it works correctly, but the next time you click the text does not change. This in itself is behaviour that I don't understand.
Now, I could use multiple handlers for slideToggle(), however, elsewhere in the code I also set intervals which load data next to "Cookie data:" and "Server data:". I don't want these intervals to do anything if the <div> is not visible so I use something like this:
this.timer_cookiedata = setInterval(function(){
if (!$savedData.is(':visible'))
{
return null;
}
// ..
});
I'm worried these intervals are not going to work properly because of this is(':visible') business. So the question is, why does this happen (else statement is ignored), and what can I do to mitigate this?
Check out the updated fiddle. When you check for visibility right after you call slideToggle, jQuery may not have updated the visibility of the element yet since the animation takes some time to finish. For this exact reason, slideToggle has a callback you can use to perform operations after the animation has finished:
$(function () {
var $savedData = $('#savedData');
$('#btn-savedData')
.click(function () {
var $button = jQuery(this);
//I'm checking the visibility in the callback. Inside the callback,
//I can be sure that the animation has completed and the visibility
//has been updated.
$savedData.slideToggle('fast', function () {
if ($savedData.is(':visible')) {
$button.html('visible');
} else {
$button.html('not visible');
}
});
});
});​

Categories