jQuery.remove() and setTimeout interaction - javascript

I have a jQuery function that creates an element when the user hovers on a element. When the mouse goes away from the element I set a timeout to destroy it. While creating and normally destroying the element does work flawlessly, the remove inside the timeout does not work and dies silently. I suspect there is some problem in the interaction between window.setTimeout and jQuery.remove, but couldn't find anything on the web.
My code:
//Element creation
function createElement(){
$(".my_class").remove()
content = compose_content() //It's an <ul>
$('<div></div>', {
class : "my_class",
html: content
}).insertAfter($("#first_row"))
}
//Element destruction
function setDestroyTimeout(){
if(perform_some_checks()){
window.setTimeout(function(){
console.log("Removing...")
$(".my_class").remove()
}, 1000)
}
}
$(".another_class").hover(createElement(), setDestroyTimeout())
The first remove (the one in the createElement function) works smoothly, while the second doesn't do anything. The Timeout works as well, I checked with the console.log instruction.

Related

Passing "this" to a function in jQuery problems

sorry but couldn't find a solution for my problem so far.
I am writing a kind of an email template editor as a little task for my boss.
$('a, span').click(function(event){
var editableElement = $(this);
if($(this).is('a')){
event.preventDefault();
}
if(editableElement.is('span')){
processType(editableElement, 'simpleText', modalContent)
When I send the 'editableElement' variable first time, everything's fine, sends object to my function, where I open up a modal window, where there is a textarea, which if i enter text and submit it using only jQuery it will put the text from the textarea (using .val()) to my desired element, in this case a "span" element which is the 'editableElement' using .text() on it. No problem, it works for the first time. After I try to edit a second span, it constantly modifies the previous span too, with whatever I enter in to the textarea (which is cleared out completely, the problem is NOT here) I've ran a fast debug with a simple foreach on the editable element, and the problem is that for some reason it keeps adding objects to the editableElement variable everytime I send it to the function. The number of spans I try to edit, the number of objects will be in my variable all with the index of 0.
Any idea what could be the cause of this?
Thanks in advance!
EDIT:
As requested the whole code in one piece which I have problem with, though it was the whole code before too, I'm in an early stage of writing it, I understand that it was hard to read though, perhaps now it is properly formatted as requested.
$(window).load(function()
{
var modalContent = $('#modalContent');
modalOverlay = $('#modalOverlay');
$('a, span').click(function(event)
{
var editableElement = $(this);
if($(this).is('a'))
{
event.preventDefault();
}
if(editableElement.is('span'))
{
processType(editableElement, 'simpleText', modalContent)
}
});
$('#codeGenButton').click(function()
{
var container = $('#codeContainer');
container.empty();
container.text(getPageHTML());
});
$('#modalClose').click(function()
{
$(this).parent().parent().animate({'opacity': '0'}, 200,
function(){
$(this).css({'display': 'none'});
});
});
});
function fillData(targetDomElement, modalObject)
{
$('#modalSubmit').click(function(){
targetDomElement.text($('#simpleTextEdit').val());
closeModalWindow();
});
}
function processType(targetDomElement, type, modalObject)
{
modalObject.empty();
if(type == 'simpleText')
{
modalObject.append("<p id='simpleTextEditTitle'>Text editor</p><textarea id='simpleTextEdit'></textarea>");
getModalWindow();
fillData(targetDomElement, modalObject);
}
}
Step by step of what it should do:
First of all, the html should not be needed for this, it does not matter, and this is the whole code honestly.
When you click on either an element of (span) or an element of (a) it triggers the function.
It will check if it was actually a (span), or an (a) element.
Currently if it is an element (a), it does nothing, not implemented yet, but if it is a (span), it will call in the processType function, which it sends the "handler?" of the element to namely "editableElement" which has been declared right after the click event, the 'simpleText' which gets send too, is just to differentiate between element types I will send to the processType function later on, and for the last, "modalConotent" is only a div container, nothing more.
Once the function gets the data first, it will make sure, that the modal window gets cleared of ALL data that is inside of it, then it will append a bit of html code as you can see, in to the modal window, which pops up right after I have appended data in to it, it is literally just a 'display: block' and 'animate: opacity: 1' nothing special there.
Lastly it will trigger the 'fillData' function, which will put my desired data from '#simpleTextField' which is only a (textarea) where you can write in, to my desired element 'editableElement' which is the element you have clicked at the first place, a (span) element after the submit, which is again, just a css of 'display: none' and 'opacity: 0' closes the modal window.
THE END.
Your problem is here
function fillData(targetDomElement, modalObject)
{
$('#modalSubmit').click(function(){
targetDomElement.text($('#simpleTextEdit').val());
closeModalWindow();
});
}
Each time this function is called it adds a new click handler with the perameters at the time the handler was created. This handler is added in addition to the already created handlers. See a demo here. After successive clicks on the spans notices how fillData is called multiple times for a single click.
To give you the best possible answer I need to know where your modalSubmit button is in relation to modalContent. Also is is modalSubmit dynamic or static on the page?
Here is a fairly hacky fix in the mean time using on and off to bind and remove the handler respectively:
function fillData(targetDomElement, modalObject)
{
$('#modalSubmit').off("click"); /*Clear Hanlders*/
$('#modalSubmit').on("click", function(){
console.log("fill data");
console.log(targetDomElement);
targetDomElement.text($('#simpleTextEdit').val());
/*closeModalWindow(); Don't have anything for this so ignoring it*/
});
}
Demo
I've solved it myself by using .submit() (of course this means adding form, and an input with the type of submit) instead of .click() when I send the request to modify the element I've clicked on originally.
I don't understand though, why it did what it did when I've used the .click() trigger.

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");
});
});

Delegate and AddClass not working

I am having a problem with getting this piece of code to work. Its built to allow for a user to click on one row to "select" it by highlighting via a different class. If another row is clicked, that one is highlighted and the current one is cleared. If one that is already highlighted is clicked again, it will be cleared back to normal.
This is within an ajax call to refresh the page, so everytime the call finishes and inserts the html for the table, this function below is then called. For some reason, every other reload of the table, this works. Ive tracked down the the line that is not functioning to be $(this).addClass(selectedUserClass) at the end of the function. The debug console log I put in there works, with the right class, but the next section does not add the class for some reason.
In firebug, the line changes from .. to , but it doesn't change? I've been looking at this for hours and I can't figure it out. Thanks for any help!
function loadUserListener(style) {
if (style == "normal") {
selectedUserClass = 'selectedUser';
selectedJQueryClass = '.selectedUser';
} else {
selectedUserClass = 'selectedUserInverted';
selectedJQueryClass = '.selectedUserInverted';
}
console.log('setting');
$("#selectuser").delegate("tr", "click", function () {
if ($(this).hasClass(selectedUserClass)) {
$(selectedJQueryClass).removeClass(selectedUserClass);
} else if ($(selectedJQueryClass)[0]) {
console.log('another');
$(selectedJQueryClass).removeClass(selectedUserClass);
$(this).addClass(selectedUserClass);
} else {
console.log(selectedUserClass);
$(this).addClass(selectedUserClass);
}
});
}
adeneo pointed out some things for me, mainly that the listener is still there even though the table is refreshed, so after every ajax call, a listener is being put on another which results in the functionality that only works on 'even' calls.
I used the 'off' function from jquery to clear the event handler every time this listener loading function is called. I also switched delegate to on to be more correct.
I essentially did this:
$('.selectuser').off("click");

jQuery assign event handler with timeouts per element

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);
});

Close popup div if element loses focus

I have the following scenario: On a label's mouseover event, I display a div. The div must stay open in order to make selections within the div. On the label's mouseout event, the div must dissappear. The problem is that when my cursor moves from the label to the div, the label's mouseout event is fired, which closes the div before I can get there. I have a global boolean variable called canClose which I set to true or false depending on the case in which it must be closed or kept open. I have removed the functionality to close the div on the label's mouseout event for this purpose.
Below is some example code.
EDIT
I have found a workaround to my problem, event though Alex has also supplied a workable solution.
I added a mouseleave event on the label as well, with a setTimeout function which will execute in 1.5 seconds. This time will give the user enough time to hover over the open div, which will set canClose to false again.
$("#label").live("mouseover", function () {
FRAMEWORK.RenderPopupCalendar();
});
$("#label").live("mouseout", function () {
setTimeout(function(){
if(canClose){
FRAMEWORK.RemovePopupCalendar();
}
},1500);
});
this.RenderPopupCalendar = function () {
FRAMEWORK.RenderCalendarEvents();
}
};
this.RenderCalendarEvents = function () {
$(".popupCalendar").mouseenter(function () {
canClose = false;
});
$(".popupCalendar").mouseleave(function () {
canClose = true;
FRAMEWORK.RemovePopupCalendar();
});
}
this.RemovePopupCalendar = function () {
if (canClose) {
if ($(".popupCalendar").is(":visible")) {
$(".popupCalendar").remove();
}
}
};
Any help please?
I would wrap the <label> and <div> in a containing <div> then do all you mouse/hide events on that.
Check out this fiddle example - http://jsfiddle.net/6MMW6/1
Give your popupCalendar an explicit ID instead of a class selector, e.g.
<div id="popupCalendar">
Reference it with #popupCalendar instead of .popupCalendar.
Now, remove() is quite drastic as it will completely remove the div from the DOM. If you wish to display the calendar again you should just .hide() it.
But your logic seems a bit overly complex, why not just .show() it on mouseenter and .hide() on mouseout events ?
This will close the entire tab page if the tab page loses focus.
How ever if you target it, it can work for something within the page too, just change the target codes.
JavaScript:
<script type="text/javascript" >
delay=1000 // 1 sec = 1000.
closing=""
function closeme(){
closing=setTimeout("self.close()",delay)
// self means the tab page close when losing focus, but you can change and target it too.
}
<!--// add onBlur="closeme()" onfocus="clearTimeout(closing)" to the opening BODY tag//-->
</script>
HTML:
<body onBlur="closeme()" onfocus="clearTimeout(closing)">

Categories