I've created a custom select using javascript as described here: http://v2.easy-designs.net/articles/replaceSelect/
The key idea is that the select is built of a containing two (in my case) s. What I want to do is add an onmouseover function that should read something like:
ulNode.onmouseout = function() {
if (ulNode.className.indexOf('selectOpen') !- -1){
selectMe(li[0]);
}
}
i.e. if the mouse leaves the ul, and the ul is open, the first element should be selected. This works fine, but this function gets called when I move my mouse between li's, even though I haven't left the containing ul. Any ideas why that might happen?
Thanks in advance!
mouseover and mouseout are wrong king of events for this case. They are fired way too often when you have other elements inside the element that has mouseout event. You need something like mouseleave and mouseenter
Mouseleave in jQuery
Try implementing some delay before the mouseout event like this:
var hover_to = null;
$("ul").mouseover(function() {
window.clearTimeout(hover_to);
// (A) your stuff...
}).mouseout(function() {
hover_to = window.setTimeout(function() {
// (B) your stuff...
},200);
});
This hopefully handles the nonwanted atomic events.
Careful with the scope in (B).
Related
I have a few functions in jQuery that control the display of a div when the parent div is moused over. The first two functions are :
$(".parentDiv").mouseover(function () {
$(this).find(".childDiv").css('visibility','visible');
});
$(".parentDiv").mouseout(function () {
$(this).find(".childDiv").css('visibility','hidden');
});
These work fine. Later, I have a click function bound to the 'childDiv':
$(".childDiv").click(function (e) {
});
I have a bunch of functionality within this function that work perfectly. However, at one point I need to disable the mouseover and mouseout functions on the parent div. I do this by:
$("#" + this.id).closest(".parentDiv").off("mouseover mouseout");
This works perfectly. But, when I try to turn it on based on other functionality in the function using:
$("#" + this.id).closest(".parentDiv").on("mouseover mouseout");
It does not turn back on. I know the selector is working, first because it works when I turn it off, but also because I added the following code and it works:
$("#" + this.id).closest(".parentDiv").css("border","1px solid #000");
Does anyone know why this is not working? I find it strange that turning the mouseover and mouseout off works, and that changing the border also works, but that turning mouseover and mouseout on does not work. What am I missing?
That is because .off doesn't 'turn off' the events, it removes event handlers.
https://api.jquery.com/off/
If you want the events back, you will have to attach them again.
I need some guidance in understanding why these functions are doing what they are doing...
1.) On my web page, I have three different panels that utilize a Slider function, which creates an unordered list that has slider functionality using next and previous anchor links. See the code below:
function Slider(id) {
var _this = this;
this.id = id;
if (!id) return false;
this.index = 0;
this.slider = $(this.id);
this.length = this.slider.children().length;
this.width = $(this.id).outerWidth();
this.totalWidth = this.length * this.width;
$(id).addClass('slideWrapper').wrap('<div class="slideViewport">').after('<div class="sliderNav">PreviousNext</div>').css({
'width': this.totalWidth
}).children().addClass('slide').css({
'width': this.width
});
$('.slideViewport a.next').click(function(e) {
e.preventDefault();
return _this.next();
});
$('.slideViewport a.prev').on(function(e) {
e.preventDefault();
return _this.prev();
});
}
If I try to run more than one of these Slider instances on a page, clicking a .next anchor will cause the clicked element and any of the elements below it to more to the next list element in their slideshows. Going to the second panel would cause all but the first to run, the third panel causes all but the first and second to run, etc. I would have expected the handler to only run for the event that I clicked on, rather than all instances of the class after it, since I am using this in my event. Any explanation as to what is going on here would be immensely helpful.
2.) Now, I've been trying to make it such that all of the Slider events DO run next() when I click on any a.next anchor on the page, rather than just run an event for the one whose anchor I have clicked. I have figured out that this code works:
$('.slideshow').on("click", "a.next", function(e) {
e.preventDefault();
return _this.prev();
});
But truth me told, I'm not really sure why this is working. My understanding is that JQuery is only looking to see if the a.next anchor is clicked, and will then pass the handling function to the $('.slideshow') selector, which makes me assume that it is selecting all instances of $('slideshow') and running the .next() function for all of them. Is that the right way to think about it?
3.) Why does the following snippet of code cause all of the slideshows to run the next() function twice, as opposed to once? I don't really like that this isn't returning anything, so I don't really want to use this particular bit of code, but I'd just like to understand it a little bit better...
$('.slideViewport a.next').on("click", function(e) {
e.preventDefault();
$('.slideshow').each(function() {
_this.prev();
}
});
Help understanding any of this code would be much appreciated. I would really like to have a better understanding of what is going on in the DOM in terms of propagation in this scenario, but everything I've tried to read has just made me feel more confused. Thanks!
This bit of code attaches a click handler to all .slideshow elements in the document.
$('.slideshow').click(function(e) {
e.preventDefault();
return _this.prev();
});
What you might have wanted was to attach the handler only to the .slideshow elements that are descendants of the slider:
this.slider.find('.slideshow').click(function(e) {
// ... handle event here ...
});
Now, about your on() statement:
$('.slideshow a.next').on("click", function(e) {
e.preventDefault();
$('.slideshow').each(function() {
_this.prev();
}
});
What you've done here is bind a click event handler to all the .slideshow a.next elements, and what the handler does is run _prev() on all of them. But you already have another handler bound to the .slideshow element from when you called $('.slideshow').click(). When the "on" handler is finished, the event continues to propagate up the DOM tree, triggering all of the click handlers on its way up, including the one you bound to the .slideshow element. And of course that handler also calls _prev().
If you want to stop event propagation, you have two choices.
call e.stopPropagation()
return false from your event handler (this tells jQuery to stop propagation and prevent the default action).
You may ask yourself, "what is the difference between click() and on('click', ...). The on() method lets you use Event Delegation. Basically, that means using a single event handler attached to one DOM node to handle events on a lot of descendant elements.
As an example, imagine you have a div with some arbitrary number of images in it, and you need to do something whenever an image is clicked. You can either (a) bind a click event handler to each image or (b) bind a handler to the div that will handle all the click events for all the images as those events bubble up the DOM tree.
$('#imageDiv').on('click', 'img', function(evt) {
// ... "this" is the image that was clicked ...
});
Delegation has the added benefit that you can add and remove images from the container, and the delegate will continue to work.
I am using jQuery events like mouseover and mouseout.
When a user fires the mouseover on the target element, this element receives a new class (with removeClass and addClass).
Then, when the mouse gets out, mouseout is fired, but the selector of the element having the mouseout event doesn't match anymore, because I changed the class.
Example :
$('span.project_unsel').mouseover(function() {
$(this).removeClass('project_unsel');
$(this).addClass('project_sel');
});
After firing the above event, class has changed and the following doesn't get fired.
$('span.project_sel').mouseout(function() {
$(this).removeClass('project_sel');
$(this).addClass('project_unsel');
});
How can I tell jQuery to "update" or "bind" again too understand this ?
Many thanks !
I would suggest doing something like this instead, to makes things less confusing:
$('span.project').hover(function() {
$(this).addClass('selected');
}, function() {
$(this).removeClass('selected');
});
That is, bind .hover to elements with the class .project, and simply add and remove the .selected class when the mouseenter (first argument) and mouseleave (second argument) events are triggered.
Take a look at .hover.
There are quite a few ways to fix this, but if you want to minimize your code changes you can just add another class to your target elements that doesn't change, then bind to that.
If you can't add an additional class for some reason, try this:
$('span.project_sel,span.project_unsel').mouseout(function() {
$(this).removeClass('project_sel').addClass('project_unsel');
}).mouseover(function() {
$(this).removeClass('project_unsel').addClass('project_sel');
});
You could also take a look at toggleClass() if you feel add/remove is inelegant.
Edit: Karim's hover solution is better than doing it via mouseout/mouseover.
I'm using jQuery to toggle the visibility of a <div> using the jQuery toggle method. The toggle is fired on the mouseenter and mouseleave event, thus creating the effect of the div to fold out on mouseenter and fold in on mouseleave. Problem is, if the user drags the mouse over the <div> a few times and then leaves the <div>, the div will toggle in and out several times. This can happen if the user accidentally moves around the mouse pointer in the <div> are. Do anyone have any idea on how I can avoid this behavior?
Thanx!
Two things:
If you're going to use both mouseenter and mouseleave I'd suggest using the hover() function; and
When using triggered animations it's a good habit to get into to use the stop() method.
So:
$("div.someclass").hover(function() {
$("...").stop().fadeIn("slow");
}, function() {
$("...").stop().fadeOut("slow");
});
Note: replace "..." with the appropriate selector for what you're toggling and use the appropriate effect (I'm using fade here). Also, this in an event handler refers to the source of the event.
You can use the more common mouseover/mouseout events to get a hover event that doesn't fire on internal mouse movements.
But don't use toggle on a mouse event, it can easily go wrong if eg. the mouse is over the element at page load time, or the mouse leaves the browser (which can allow the mouse to leave the bounds of the element without firing a mouseout). Have separate function for over which shows the content, and out which hides it.
Better: just use the hover() method which is meant for exactly this purpose.
Aside from the correct answer by Cletus, i'd like to point out that using mouseenter and mouseleave events is not wrong. The trick only resides into the stop() method, in fact we could still do:
$("div.someclass").on("mouseenter", function() {
$("...").stop().fadeIn("slow");
});
$("div.someclass").on("mouseleave", function() {
$("...").stop().fadeOut("slow");
});
Here is a jsFiddle example :)
I've been working on some code where I trigger the code on hover/unhover. Then I decided to trigger the same code also on focus/blur. Usually with hover/unhover only, I go for the usual hover comma no unhover format. But this time since I was trying to add focus/blur, I had to use bind and use this.bind with the second part too, like this:
$.fn.gogogo = function (msg) {
$(this).bind("hover focus", function(){
$("#screen").html(msg).show();
});
$(this).bind("unhover blur", function(){
$("#screen").html("").hide();
});
}
The problem was that no matter what I did, hover/unhover didn't take. I had to revert back to mouseover/mouseout like this. The code is identical except for the words hover/unhover vs. mouseover/mouseout
$.fn.gogogo = function (msg) {
$(this).bind("mouseover focus", function(){
$("#screen").html(msg).show();
});
$(this).bind("mouseout blur", function(){
$("#screen").html("").hide();
});
}
I thought hover/unhover was just the jquery abstraction of mouseover/mouseout. How come the behavior is different here: hover/unhover breaks my code, while mouseover/mouseout is ok?
thanks.
There is no event called hover.
The hover method is a convenience method that binds event handler to the mouseenter and mouseleave events.
If you open the jQuery source, you'll see that the hover method is defined like this:
hover: function(fnOver, fnOut) {
return this.mouseenter(fnOver).mouseleave(fnOut);
},
You should bind to the mouseenter and mouseleave events instead.
EDIT: The difference between mouseenter and mouseover is that mouseenter (and mouseleave) don't bubble. This means that you'll get a mouseover event if the mouse moves into any element inside the one you bound to (which is probably not what you want), whereas you'll only get a mouseenter event if the mouse entered that element itself. For an example, see the documentation.
There is no "hover" event. That's just a convenience routine.