How to add another click function after criteria are met - javascript

I have a form where people are essentially booking rooms. they have one room instantly available and then there are 2 hidden (inputs disabled) fieldsets below. With a view to be able to add any number of extra room fieldsets in the future, want to be able to change the function of the click event once all the current extra rooms are visible (ie. control the visibility of a separate div/link that would link to a large groups booking page).
i hope that makes sense.
if you need the html just ask but here is what i have so far:
$('.add-room').on('click', 'a', function(event){
event.preventDefault();
var next_to_show = $('#homepage-search-form .extra-room:hidden:first');
$(next_to_show).slideDown(500);
$(next_to_show).find('select').prop('disabled',false);
var total_extra_rooms = $('.extra-room').length;
var extra_rooms_visible = $('.extra-room:visible').length;
if (total_extra_rooms == extra_rooms_visible) {
$('.add-room').addClass('test');
}
});
$(document).on('click', '.test', function(){
//new click function here
});
The problem I have is that once the total_extra_rooms is equal to the extra_rooms_visible variable the .test function is called. I want it to be called on the next click of the .test element.
Another thing to be aware of is that once an extra room fieldset is visible it contains a link that can remove it should the user change their mind.
Any help would be fantastic.

You can do this:
if (total_extra_rooms == extra_rooms_visible) {
setTimeout(function() {
$('.add-room').addClass('test');
}, 0);
}
Which will wait until after the current operation to add the test class.

Related

Customize dynamically created elements adding an option choosed via popup

I have a little code that creates elements (rectangles) and when I pass the mouse over them, a "customize yellow button appears on it". When I click this button, a popup with colors let us choose a colour to add in the selected rectangle.
Basically, I have 3 elements... click on 1 of them and choose a colour. This action, clones de tag and set it into the selected item. This works fine.
The problem appears when I click in the second item (or third)... I choose a new different colour but the action changes the selected rectangle and the sibling -applies to all elements that already have a cloned - (like propagation)...
I need to customize every single rectangle with its own colour and not all of them with the same. I pasted a little code here and a working (wrong) link in jsfiddle.
The action executes "on" cause the items are created dynamically (in this example I set them manually.
Can anybody help me? I don't understand what I'm doing wrong.
https://jsfiddle.net/martiniglesias/20Laxn84/2/
$(document).on("click","a.person",function (e)
{
e.preventDefault();
e.stopPropagation();
var elrel=$(this).attr('rel');
var elem=$("#ch_dndBoard1 span[data-id="+elrel+"]");
var elemrel=elem.attr("rel");
if (elemrel=="f1E")
{
$("body").append ("<div class='overlay'></div>").show();
$(".persE").fadeIn("fast");
$(".persE li").click(function(f)
{
f.preventDefault();
f.stopPropagation();
var ese=$(this).closest("li");
if ($(this).hasClass("nope"))
{
elem.find('b').fadeOut("slow",function() { elem.find('b').remove(); });
}
else
{
elem.find('b').remove();
var added=ese.find("b").clone();
added.css({"left":0+"px","top":+5.48+"px","position":"absolute"});
$(added).insertAfter(elem.find('em'));
}
$('.persE').fadeOut("fast",function(){ $(".overlay").remove(); });
});
}
return false;
});
I expect that every single rectangle can choose its own colour cloning it from the popup. For example, I want, rect1 blue, rect2 without color, rect3 red...
Thank you!
PS: Please, forgive my poor english :(
You have this issue because you are adding a click event listener to .persE li each time you click on a a.person.
You need to remove that listener when all your logic is over:
$(".persE li").click(function(f) {
// Your code
$(".persE li").off('click');
});
Be aware that if you listen an other click event with a different logic, that one will be destroyed too.
In order to avoid this, you need to reference your different logics in function:
const changeColorEvent = (e) => {
// Your code
$(this).off('click', changeColorEvent); // Here, "otherEvent" will still exist.
};
const otherEvent = (e) => {
// Different logic here
}
$(".persE li").click(changeColorEvent);
$(".persE li").click(otherEvent);

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

Exclude a radiobutton from .click() of div

I am trying to make and area on my page selectable, but inside there is a radiobutton which i dont want to be part of the selectable area. I have it setup at the moment to allow the user select the area but this also includes the radiobutton:
$(document).ready(function () {
jQuery(document).on('click', 'label[title^="id_"]', function(e) {
$(this).addClass('hide');
var current = "s_" + $(this).attr('title');
$("label[title='" + current + "']").removeClass('hide');
});
});
This basic idea of the code at the moment is hide the selected div and show another. What i want is to exclude the radiobuttons inside this div from the click event.
I tried this: but it prevents clicking of the radiobuttons
if ($(e.target).is('input[type="radio"]')) {
e.preventDefault();
return;
}
As said by Jon in comments(and also my personal answer)
Your code should be:
if ($(e.target).is('input[type="radio"]')) {
return;
}
Put that in opening of the function body of the click event handler.
The return call, will terminate the function so it wont progress further.

Run a click function only once within another function

So I'm running a function when someone clicks on an element with a certain class name (10 of these classes). Then within that function I have another click listener for elements with another class name (another 10). I want this second click function to only occur once after that first click.
So ideally someone would click something from a set of 10, I'd then pull data from that and apply it when someone clicks an element from another set of 10. And then in order to click that second set of 10 they will have to click something from the first set again.
I'm having trouble pulling that off and I've tried some sort of .one implementation.
$('.words_col .wrap').click(function(){
theFunction(this)
})
Then
function theFunction(e) {
$('.examples_col .wrap').click(function(){
//allow only once.
})
}
$('.words_col .wrap').click(function(){
theFunction(this);
});
function theFunction(e) {
var oncer = true;
$('.examples_col .wrap').click(function(){
if(!oncer){return false;}
oncer = false;
//allow only once.
})
}
I add this as an alternative to .one because you have more than one element being selected, and .one will allow one click on each, instead of one click total.
one() will attach the click only once:
$('.words_col .wrap').on('click', function(){
$('.examples_col .wrap').one('click', function(){
//works only once
});
});

How to see if a user has clicked on two elements?

I am working on a selection menu. I want the user to be able to select two different options and have something displayed depending on what they have selected. Right now, I am just using images to represent the selections. I am wondering if there is a way to see if a user has selected/clicked two of the images consecutively. After the user has clicked two pictures, I want them to be redirected to another page and have something displayed.
/*clicked on certain two images*/(function(){
document.location.href="#page";
$('option').css('display', 'inline');
});
Use a class to mark selection and after each click, check to see if two are selected:
$('img').click( function() {
$(this).toggleClass('selected');
if($('img.selected').length == 2) {
alert("Two images selected!");
// Or redirect to new page, etc.
}
});
Demo:
http://jsfiddle.net/G9NXA/
This is, of course, a generic solution. You probably don't want to use img as your selector, but some other class that defines your selectable images.
If you want to make sure the clicks/selections are consecutive clicks, simply clear the selection if the user clicks anywhere else:
Demo: http://jsfiddle.net/G9NXA/1/
And if you meant spatially consecutive, rather than temporally consecutive, here is another example:
Demo: http://jsfiddle.net/G9NXA/2/
If you don't want to store javascript state, you could try adding a marker class to the selected images on click and count how many are found then redirect.
This is the easiest method I came up with:
var tracker = [];
$('*').click(
function(e){
e.stopPropagation();
var lastEl = tracker.length ? tracker.pop() : '',
tag = this.tagName.toLowerCase();
tracker.push(tag);
if (tag == lastEl) {
doSomething();
}
else {
return false;
}
});​
JS Fiddle demo.
This applies a click handler to all elements, though (and prevents propagation/bubbling of the event, so you should be more specific in your selector than I have here); it also stores the tag-name each clicked-element in an array and then matches the tag-name of the currently-clicked element with the tag-name of the previously-clicked item.
If the two are the same, then the if evaluation returns true and the doSomething() is executed; otherwise the click-handler returns false, and nothing happens.
This depends. If the images are sibling nodes you could check .prev() and .next(). You the image on click with a class like 'imageClicked'. Then check if the .prev() or .next() also has that class.
$('img').click( function() {
var clickedImg = $(this);
clickedImg.toggleClass('imageClicked');
if(clickedImg.hasClass('imageClicked')) {
if(clickedImg.prev().hasClass('imageClicked') ||
clickedImg.next().hasClass('imageClicked'))
alert('Siblings Selected!');
}
});

Categories