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

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

Related

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

How to check if all divs have a specific background-color?

I've dynamically added a bunch of divs of a certain class to my page, and they change color when the user hovers over them. I want to trigger a specific function when the last one has been set to a specific background color, but after messing around for some time I am still unsuccessful. If anyone could provide some insight, I would be very appreciative. The code below is from my latest attempt.
var counter=0;
$(".orb").each(function () {
if ($(this).css("background-color") === "#ede74a") {
counter=counter+1;
}
}
if (counter === orbarray.length) { //all backgrounds have been set
executeFunction();
}
Would be simpler to add a class to the elements and keep track of class
$(document).on('mouseenter','.orb',function(){
var $orb = $(".orb");
$(this).addClass('hovered');
if($orb.filter('.hovered').length === $orb.length){
alert('All hovered!');
executeFunction();
}
});
It's not clear exactly what is happening with the specific colors mentioned but this could be adapted for multiple classes if need be.
Note that browsers don't store colors as hex values....they store them as either rgb or rgba depending on browser
It's not clear from the code you've provided what orbarray equals. It's also odd that you're checking to see if counter equals the length of that array outside of the $.each() callback.
But so you're saying you need to trigger a specific function when the last <div> has been hovered over. Sounds like you want a handler for the mouseenter event, and to then check whether all the elements have been hovered over in that handler. Also, instead of applying inline CSS, if you apply a class you'll have a handier way to check whether the element has been hovered.
Observe:
function checkOrbs() {
var $orbs = $('.orb'),
$activeOrbs = $orbs.filter('.active');
if ($orbs.length === $activeOrbs.length) {
executeFunction();
}
}
$('.orb').on('mouseenter', function(e) {
var $target = $(e.target);
$target.addClass('active');
checkOrbs();
});
And then just apply your background color via CSS:
.active {
background-color: #ede74a;
}

Javascript hide element (when clicked outside) with certain conditions

So there is much questions here about hiding div when you clicking outside of it. But I have one thing, there is a div(accounts-edit-table-name-edit) which showing hidden div(account-edit-group) on click first. And then - if I will click somewhere else out of the div(account-edit-group) - it must hide. Here is my code where I am trying to do two different conditions (OR):
$(document).click(function(event) {
if($(event.target).parents().index($('.account-edit-group')) == -1 || $(event.target).parents().index($('.accounts-edit-table-name-edit')) == -1)
{
if($('.account-edit-group').is(":visible"))
{
$('.account-edit-group').removeClass('acc-edit-f');
alert("hiding")
}
}
});
HTML:
<div class="accounts-edit-table-name-edit">"button"</div>
<div class="account-edit-group">block</div>
(class "acc-edit-f" just contains "display: block")
Well, if I will click on the div with class "accounts-edit-table-name-edit" - system will immediately show me alert("hiding") though it must look through the conditions and ignore that. Is there any ways to fix this?
see the jsfiddle if it is what you want :
http://jsfiddle.net/5E6C6/2/
$(event.target).parents().index($('.account-edit-group')) //always return -1
$(event.target).parents().index($('.accounts-edit-table-name-edit')) // this too
That's because parents doesn't include the first element, which is e.target.
Here what you could do :
if(!$(event.target).closest('.account-edit-group, .accounts-edit-table-name-edit').length)
try using .hide() instead of removeclass
$( ".account-edit-group" ).hide();
Here is what you need:
add the element that is shown/hidden inside the button
stop event propagation when one of the two elements are clicked
bind onclick event on the document and hide all child elements
here is a working example i wrote for you:
http://jsfiddle.net/T2b4z/2/
$(document).click(function(event) {
var clickedElement = $(event.target);
if (clickedElement.hasClass('accounts-edit-table-name-edit') || clickedElement.parents().hasClass('accounts-edit-table-name-edit')) {
$('.account-edit-group').removeClass('acc-edit-f');
return false;
}else {
$('.account-edit-group').addClass('acc-edit-f');
}
});

How to add another click function after criteria are met

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.

Using :not to select all elements except inside box with certain class

I have an overlay that I want to hide on mousedown outside it.
Here is what I tried out but unfortunately :not selector is not working as I expected.
$('body:not(.overlay)').one('mousedown',function(){
//hide overlay here
});
I also tried $('* :not(.overlay)') but same issue.
The overlay gets hidden even when clicking inside the overlay box
$(document).on( "mousedown.hideoverlay", function(e){
if( $(e.target).closest(".overlay").length === 0 ) { //overlay wasn't clicked.
//hide overlay
$(document).off("mousedown.hideoverlay");
}
});
Your selector body:not(.overlay) matches the body element if it doesn't have a class overlay, I'm assuming what you meant was its descendant without the class overlay:
$('body :not(.overlay)'); //note the space - descendant selector
The problem with such an assignment is that it matches too many elements (in particular, parents of selected elements). Tehnically, even clicking on any container div would match the selector, fiddled. This happens because even clicks on elements with overlay class continue propagating up the DOM.
I agree with other suggestions here i.e. it's appropriate to listen to all clicks and do nothing if the selector doesn't match, however preventing event propagation on them could interfere with the rest of the page's logic.
I'd rather advocate an approach where there is an explicit subset of "overlayable" items that could be clicked on - and filter them with :not(.overlay) selector:
$('.some-generic-container-name:not(.overlay)')...
Try the .not() function: http://api.jquery.com/not/ . It specifically removes elements from a selected group which is probably the problem you are getting here. Saves having to do complex if's etc to solve this
$('*').not('.overlay').on('mousedown', function(){
alert("here");
});
Edit
Heh, Didn't read the question fully:
$(document).on('mousedown', function(e){
var target = $(e.target);
if(!target.parents().hasClass('overlay') && !target.hasClass('overlay')){
// hide stuff
}
});
Edit: I prefer to use click here (Dunno why):
$(document).on('click', function(e){
var target = $(e.target);
if(!target.parents().hasClass('overlay') && !target.hasClass('overlay')){
// hide stuff
}
});
It just looks nicer in my opinion, call me weird...

Categories