check if this element OR this element is hovered with jQuery - javascript

I have two separate elements in my DOM that require a change when either one is hovered. When the link is hovered, the image src needs to change (easy) AND the link color needs to change. When the image is hovered, the same effect needs to happen (image src changes & link color changes)
This is actually quite easy to do, but I have a feeling there is a MUCH easier way to do it then the approach I am taking. Currently I am going through each of the 8 elements and testing if they are hovered individually. This works good, but there is so much more jQuery here than I feel there should be.
I tried adding the attribute onmouseover to both of the elements and triggering the same function, but for some reason the function wasn't triggering.
Is there a way to test if either element is hovered and trigger a function if either one is? Something like this:
if($(#elm1).hover() || $('#elm2').hover()) {
//effect here
}
OR
if($('#elm1').is(':hover') || $('#elm2').is(':hover')) {
//effect here
}
I tried my example code above, but wasn't getting any results. Is there a way to do this or am I stuck with checking each individual element?

It works, you're just running it on page load, not when the mouse moves
$(document).mousemove(function() {
if($('#elm1').is(':hover') || $('#elm2').is(':hover')) {
// Do stuff
}
else {
// Revert do stuff or do other stuff
}
});
Example here
But, as other's have said, jQuery already knows the hover state, so doing it this way would be unnecessary. In practice you should use the same selector to apply the function to it
$('#idOne, #idTwo').hover(function() { /* Do stuff */ });

I would register events on mouseover and whenever the event happens trigger a function
$("#elm1, #elm2").on("mouseover", function () {
// effect here
});

Why not simply add the handler to both elements like this :
$('#elm1, #elm2').hover(function(){
// ... do stuff here ...
});
Should work . . .

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 use javascript to select (a) child(ren) element(s) of a hovered element?

assuming I have a (very large) div tag and inside the div tag I have a (normal size) button, now I want to be able to create a shortcut that if a user is hovering over the div tag, they can press return key to click the button.
$(window).keypress(function(e){
if (e.keyCode == xxx) {
$('div').hover(function(){
$('this button').click();
});
}
});
This is how I imagine it might look like in jQuery (didn't work obviously). I am open to suggestions. jQuery solutions are fine, plain javascript solutions are even better.
It's actually pretty easy.
$(window).keypress(function(e){
if (e.keyCode == xxx) {
$('div:hover button').click();
}
});
Don't use .hover() or .on('hover') because they are simply not selectors.
You can use .is(":hover") within your keypress handler to determine if the proper div is being hovered:
$(window).keypress(function(){
if($("#target").is(":hover")){
alert("pressed!");
}
});
http://jsfiddle.net/y7joukzw/2/
(NOTE: Make sure you click within the "result" frame to ensure it is the active frame when testing the jsfiddle)
Rather than checking for hover on every keypress, you're better off reversing the order of event checking so that you only incur the keypress overhead while the user is hovering. Something like:
function checkKeypress(e) {
// Check keypress and perhaps do something
}
$('div').hover(
function(){
$(window).keypress(checkKeypress);
},
function() {
$(window).off('keypress', checkKeypress);
}
);

Toggle focus to follow toggleClass

I'm doing a simple show/hide on a search form that uses jQuery's toggleClass() and CSS to show and hide the form. That's easy enough, something like:
$('#site-search-toggle').click(function(e){
$('#site-search').toggleClass('search-open');
e.preventDefault()
});
What I'd like to do but am having a hard time figuring out is to put focus on the search input when the form is shown and remove the focus from the search input when the form is hidden.
It's easy to add focus:
$('#site-search-toggle').click(function(e){
$('#site-search').toggleClass('search-open');
$('#site-search input[type="search"]').focus();
e.preventDefault()
});
But I'm stuck at how to remove it when $('#site-search-toggle') is clicked again to hide the form.
Just found this thread as I've been banging my head against this problem for a while now.
I've found a very simple way to do this, and essentially all you need to do is provide a click handler. When the element is clicked, you toggle the class which controls the 'focused' state, but you also programatically focus the element:
document.getElementById('myelement').addEventListener('click', function() {
this.classList.toggle('focus'); // or whatever...
this.focus();
});
You will need to give the element some sort of 'tabindex' value, probably 0 or -1.
And then, you provide a 'blur' handler, which just removes the 'focus' class whenver the user navigates away from the element.
document.getElementById('myelement').addEventListener('blur', function() {
this.classList.remove('focus');
return false;
});
Works like a dream!
I'm sorry that this is not a jQuery answer, but it should be easy enough to adapt - I just don't use it...
Danny
OK I figured this one out, or at least I found a way to do what I need to do. I added a second class, search-closed, toggled both classes, then used each class to focus or blur the field, something like this:
$('#site-search').addClass('search-closed');
$('.site-search__toggle').click(function(e){
// toggle both classes
$('#site-search').toggleClass('search-open search-closed');
// set focus when form is visible, .search-open
// use setTimeout to make sure the cursor actually gets in there
// don't know why, but it works
setTimeout (function(){
$('#site-search.search-open .site-search__input').focus();
}, 20);
// blur when the form is not visible, .search-closed
$('#site-search.search-closed .site-search__input').blur();
});
Try this:
$('#site-search-toggle').click(function(e){
$('#site-search').toggleClass('search-open');
if ($('#site-search').hasClass('search-open')) {
$('#site-search input[type="search"]').focus();
} else {
$('#site-search input[type="search"]').blur();
}
e.preventDefault();
});

Stop propagation for a specific handler

Let's say I have custom dropdown(). When the button is clicked I want to bring up the menu, and when the user clicks outside of the menu I want it to close. So I do something like this:
$(myDropDown).mousedown(dropDownMouseDown);
$("html").mousedown(htmlMouseDown,myDropDown);
function dropDownMouseDown(event) {
event.target.open();
event.stopPropagation();//I need this line or else htmlMouseDown will be called immediately causing the dropDown-menu to close right before its opened
}
function htmlMouseDown() {
this.close();
}
Well, this works. But what if I add two of these? If I click to open the first, then the same on the second then both will be open because dropDownMouseDown stops the propagation so that htmlMouseDown never gets called for the first.
How do I get around this?
If I only had these two then adding some logic for that would of course be easy, but if the quantity is dynamic? Also I might not want to call event.stopPropagation() because it will do strange stuff to other libraries I'm using which listen for that event too?
I also tried putting this line:
$("html").mousedown(htmlMouseDown,myDropDown)
inside the dropDownMouseDown-handler but it will be called immediately anyway once the bubbling reaches the html-element.
Assuming you have a selector for your dropdows, (let's say ".dropdown"), I would try to use '.not()'
$('.dropdown').mousedown(dropDownMouseDown);
$("html").on('mousedown', htmlMouseDown);
function dropDownMouseDown(event) {
event.target.open();
}
function htmlMouseDown(event) {
$('.dropdown').not($(event.target)).close();
}
Here is a fiddle in the same idea with css classes :
http://jsfiddle.net/eFEL6/4/
What about using a variable that contains the last openened one ? There are probably many other ways of doing this, but here is a way I could think of:
var lastOpened = null; // initially nothing is open (unless something is)
Then:
function dropDownMouseDown(event) {
if (lastOpened != null) { // if one is still open
lastOpened.close(); // close it
lastOpened = null; // nothing is open anymore
}
event.target.open();
lastOpened = event.target; // now this one is open
event.stopPropagation();
}
function htmlMouseDown() {
this.close();
lastOpened = null; // nothing is open
}
That should work in a way that the last opened one always close itself before opening a new one.
Thanks for the answers. They're really appreciated. I did figure out a way of doing it that I'm satisfied with. Here's how:
$(myDropDown).mousedown(dropDownMouseDown);
$("html").mousedown(myDropDown,htmlMouseDown);//Pass in the dropDown as the data argument, which can then be accessed by doing event.data in the handler
function dropDownMouseDown(event) {
event.target.open();
}
function htmlMouseDown(event) {
if (event.target!=event.data)//event.target is the element that was clicked, event.data is set to the dropdown that this handler was added for. Unless these two elements are the same then we can...
event.data.close();///close the dropdown this handler was added for
}
Can't believe I didn't think of that. In my case though the element that opens/closes has child-elements so event.target could be one of the child elements instead of the element that the handler was attached to. So I changed my html-element-handler to this:
function htmlMouseDown(event) {
var element=event.target;
while (element) {
if (element==event.data)
return;
element=element.parentElement;
}
event.data.hide();
}

Trigger event on specific condition

I have a div that will serve as container to other element, I have buttons that add element to that div.
Please see the demo for a get an idea about it.
So, what I want to do is to check before adding a new element is the div reached a maximum number of elements that I define, let's say 4.
I can check this condition before every add, but I am sure this is not the best way (we learned that if the code contains copy/paste then is not the best solution) Also, this is just a sample, in my case, I have many buttons..
Is there a way to have a listener like this?
$('#container').bind('divFull', function(){
//My code
});
So that I can disable buttons..
First, you have to listen to DOM change event, then you can trigger a custom event based on the number of children
$('#container').bind('DOMSubtreeModified', function(){
if($(this).children().length>=4){
$(this).trigger('divFull');
}
});
then you can bind to your custom divFull event
$('#container').bind('divFull', function(){
alert('container is full');
$('button').prop('disabled',true);
});
a working demo based on your example
I change a bit the #skafandri method because the event DOMSubtreeModified doesn't work on IE < 9 and it's depreciated.
The main change is to create a function which will call the divFull event if their is 4 children in the container.
var checkFull = function() {
if ($container.children().length === 4) {
$container.trigger('divFull');
}
}
$('#button1').click(function(){
$container.append('<div class="element">some text</div>');
checkFull();
});
Here is the demo.

Categories