Checking for visibility of a toggled element - javascript

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

Related

How to slowly show-hide with jquery prop

When I click the button, I want to hide that button and show the div. I want this to be slow.For this:
$('#add_note').on('click',function (){
$(this).hide(200);
$('#add_note_form').show(200);
})
i used this.
But, I want to make this using prop('hidden',true/false).
$('#add_note').on('click',function (){
$(this).prop('hidden',true);
$('#add_note_form').prop('hidden',false);
})
It works like this but is it possible to do it slow?
You can't animate a slow transition using the boolean hidden property.
However you can use fadeOut() instead, then set the property in the callback when the animation completes:
$('#add_note').on('click',function() {
$(this).fadeOut(1000, function() {
$(this).prop('hidden', true);
});
$('#add_note_form').fadeIn(1000);
})

How to trigger a class change event when the code that changes the class is out of reach

I need to trigger an event on a class when that class changes
The only known change noticed in the DOM is that the class obtains a second class (say the class is "selectable", it becomes "selectable selected")
https://jsfiddle.net/zn1xj7wb/1/
In this fiddle, the blue squares may be selected and the css change happens when the class changes (adds "selected")
The goal is to be able to do something in another part of my code like that:
$("[class*='selectable']").on('classChange', function() {
//do stuff like change the background color or add text
//alert("this selectable div has been selected");
});
I am unsure how to proceed as jquery has no event for a class change, and I cannot add "classChange" the trigger to the hidden part of the code that adds and removes the "selected" class for it to be picked up by my code.
EDIT: the reason I need the trigger to be the class change is that it is a graph that uses up the first click to change the class (select the node of the graph) and so a first click on the div of that class does not register, only the second time, and I cannot have to click twice to //do stuff.
I'm not sure I understand your problem, but what I would do is atach the event to the document, like this:
$(document).on("click",".selectable", function() {
//do your stuff here
});
Now, as I've read you need to do something right after you add the class "selected" to "selectable", so you could do it in the function by checking wether it has the class or not and then do your stuff after you add the class "selected".
$(document).on("click",".selectable", function() {
if($(this).hasClass("selected")){
$(this).removeClass("selected")
//do your stuff
}else{
$(this).addClass("selected")
//do some different stuff
}
});
EDIT: Okay, so that won't work (see comments). However, I was able to come up with another solution. While you could regularly scan the whole DOM for changes using an external library, in this instance, you can make the app more performant by limiting your scope to just the selectable items.
What the following code does (jsfiddle link below) is take an initial sampling of the selected elements on the page. Then, once per event loop, it re-samples those selected elements. For each element that wasn't there before, it triggers a custom event:
$(document).ready(function() {
$('.selectable').on('customSelectEvent', (e) =>{
console.log("hello, world!");
// Do your stuff here
});
// Get the starting list of selectable elements
var selecteds = $('.selected');
// Using setInterval to make sure this runs at the back of the event loop
setInterval(() => {
let loopSelecteds = $('.selected');
$.each(loopSelecteds, function(loopIndex, loopSelected) {
let alreadySelected = false;
$.each(selecteds, function(index, selected) {
if ($(selected).get(0) === $(loopSelected).get(0)) {
alreadySelected = true;
}
});
if (!alreadySelected) {
$(loopSelected).trigger('customSelectEvent');
}
});
selecteds = loopSelecteds;
}, 0);
})
Some things to note here:
setInterval(()=>{...}, 0) is being used to cast this operation to the back of the event loop, so it will evaluate once per turn. Use caution when doing this, because if you do it too much, it can impact performance.
$().get(0) === $().get(0) is testing the DOM elements to see if they are the same element. We don't want to trigger the event if they are. Credit: https://stackoverflow.com/a/19546658/10430668
I'm using $.each() here because it's intelligent enough to handle collections of jQuery objects, which other loops weren't (without some fiddling).
Someone spot check me on this, but you may be able to put the custom event listener elsewhere in the code.
JS Fiddle: https://jsfiddle.net/zn1xj7wb/15/
This is my first answer, which doesn't work in this use case. I'll include it so that users who aren't so stuck can benefit from it:
Is there any reason you can't bind another listener to the click event
and test if it's got the right class? Such as:
$(document).ready(function() {
$(".selectable").click((e) => {
const currentElement = $(e.currentTarget);
// This is a little tricky: don't run the code if it has the class pre-setTimeout()
if (currentElement.hasClass('selected')) {
return;
}
// Using setTimeout to cast the evaluation to the end of the event loop
setTimeout(()=>{
if (currentElement.hasClass('selected')) {
// Do your things here.
alert("selected!");
}
},0);
})
})

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

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

jQuery: Can't cache onclick handler?

I've got a step-by-step wizard kind of flow where after each step the information that the user entered for that step collapses down into a brief summary view, and a "Go back" link appears next to it, allowing the user to jump back to that step in the flow if they decide they want to change something.
The problem is, I don't want the "Go Back" links to be clickable while the wizard is animating. To accomplish this I am using a trick that I have used many times before; caching the onclick handler to a different property when I want it to be disabled, and then restoring it when I want it to become clickable again. This is the first time I have tried doing this with jQuery, and for some reason it is not working. My disabling code is:
jQuery.each($("a.goBackLink"), function() {
this._oldOnclick = this.onclick;
this.onclick = function() {alert("disabled!!!");};
$(this).css("color", "lightGray ! important");
});
...and my enabling code is:
jQuery.each($("a.goBackLink"), function() {
this.onclick = this._oldOnclick;
$(this).css("color", "#0000CC ! important");
});
I'm not sure why it's not working (these are good, old-fashioned onclick handlers defined using the onclick attribute on the corresponding link tags). After disabling the links I always get the "disabled!!!" message when clicking them, even after I run the code that should re-enable them. Any ideas?
One other minor issue with this code is that the css() call to change the link color also doesn't appear to be working.
I wouldn't bother swapping around your click handlers. Instead, try adding a conditional check inside of the click handler to see if some target element is currently animating.
if ($('#someElement:animated').length == 0)
{
// nothing is animating, go ahead and do stuff
}
You could probably make this a bit more concise but it should give you an idea... Havent tested it so watch your console for typeos :-)
function initBack(sel){
var s = sel||'a.goBackLink';
jQuery(s).each(function(){
var click = function(e){
// implementation for click
}
$(this).data('handler.click', click);
});
}
function enableBack(sel){
var s = sel||'a.goBackLink';
jQuery(this).each(function(){
var $this = jQuery(this);
if(typeof $this.data('handler.click') == 'function'){
$this.bind('goBack.click', $this.data('handler.click'));
$this.css("color", "lightGray ! important");
}
});
}
function disableBack(sel){
var s = sel||'a.goBackLink';
jQuery(s).each(function(){
var $this = jQuery(this);
$this.unbind('goBack.click');
$this.css("color", "#0000CC ! important");
});
}
jQuery(document).ready(function(){
initBack();
jQuery('#triggerElement').click(function(){
disableBack();
jQuery('#animatedElement').animate({/* ... */ }, function(){
enableBack();
});
});
});

Categories