How to resolve mouse hover on overlapping images in jQuery? - javascript

This question seems somewhat related to
How do I check if the mouse is over an element in jQuery?
jQuery check hover status before start trigger
but still not quite. Here's the thing: I'm writing a small gallery and at some point the user clicks on a thumbnail. The large version of the image shows as an inside of a full-screen . So far, so good. When the mouse hovers over that I show three images: close, left, right, intended to navigate through an album; when the mouse leaves the image or the navigation images, the three navigation images fade.
These three navigation images partially overlap with the main image, e.g. the close image is a circle in the upper left corner. And that's the tricky part.
The mouseleave triggers whenever the mouse moves from the main image off the side, or from the main image onto one of the three small overlapping images. The mouseenter triggers for each of the small overlapping images as expected.
However, this creates a funky blinking effect because on mouseleave of the main image I hide() the three small images, which immediately triggers a mouseenter for the main image because of the overlap and the mouse still being on top of the main image.
To avoid that, I tried to determine if, upon mouseleave of the main image, the mouse has moved onto one of the small overlapping images. For that, I used this code:
main_img.mouseleave(function() {
if (!hoverButtons()) {
doHideButtons();
}
});
function hoverButtons() {
return (close_img.is(":hover")) || (left_img.is(":hover")) || (right_img.is(":hover"));
}
This works great on Safari and Chrome, but not on FF and IE where the images still blink. Further noodling around posts, it seems that ":hover" is the problem here as it is not a proper selector expression but rather a CSS pseudo class?
I tried to work with switches that I flip on/off as I mouseenter/mouseleave the various images, but that doesn't work either because the events seem to trigger in different orders.
How do I go about this? Thanks!
EDIT: I might have to clarify: before the navigation buttons are shown, I set their respective left and top attributes in order to place them in dependence of the main image's position. That means I need to do some work before I can call .show() on a jQuery selector. I tried to add a new function .placeShow() but that didn't quite work with respect to selectors like $(".nav-button:hidden").placeShow().

You can try with this:
$("#main, #small").mouseenter(function() {
$("#small:hidden").show();
}).mouseleave(function(e) {
if(e.target.id != 'main' || e.target.id != 'small') {
$('#small').hide();
}
});
DEMO

Here is what I ended up doing. There are four images I use in my slide show: the main image, and then left, right, close button images.
main_img = $("<img ... class='main-photo slide-show'/>");
close_img = $("<img ... class='nav-button slide-show'/>");
left_img = $("<img ... class='nav-button slide-show'/>");
right_img = $("<img ... class='nav-button slide-show'/>");
The classes here are essentially empty, but help me to select based on above answers. The main image then shows without navigation buttons, and I attach these event handler functions:
$(".slide-show").mouseenter(function() {
$(".photo-nav:hidden").placeShow();
});
$(".slide-show").mouseleave(function() {
$(".photo-nav").hide();
});
where the placeShow() moves the navigation buttons into their respective places. This function is defined as follows:
$.fn.placeShow = function() {
var pos = main_img.position();
var left = pos.left;
var top = pos.top;
var width = main_img.width();
var height = main_img.height();
close_img.css({ "left":"" + (left-15) + "px", "top":"" + (top-15) + "px" }).show();
left_img.css({ "left":"" + (left+(width/2)-36) + "px" , "top": "" + (top+height-15) + "px" }).show();
right_img.css({ "left":"" + (left+(width/2)+3) + "px", "top":"" + (top+height-15) + "px" }).show();
}
This worked so far on Safari, IE, FF, Chrome (well, the versions I've got here...)
Let me know what you think, if I can trim this code more, or if there are alternative solutions that would be more elegant/fast. The final result of all this is on my website now.
Jens

Related

Scrolling the window to place a div at mouse coordinates

I've got an expand/collapse situation, which inserts/removes a significant chunk of content onto a page. That ~1800 vertical pixels disappearing has a disorienting effect, as suddenly my user will find himself way down the page in an area where previously he may not even have been.
So, following the 'collapse' click, I'd like the 'expand' div to be at the position where the 'collapse' div was previously.
Now, I can obtain the coordinates of the mouse when an item gets clicked:
var btnCollapse = document.getElementById('detail-collapse');
var btnExpand = document.getElementById('detail-expand');
btnCollapse.addEventListener('click', (e) => {
console.log('expand button at page Y ' + btnExpand.offsetTop);
console.log('collapse button at page Y ' + e.pageY);
console.log('clicked at window Y ' + e.clientY);
});
Using btnExpand.scrollIntoView() feels too inaccurate. I want the expand div at an exact spot.
Using .scrollBy(0,1800) (or -1800) or .scroll(0,5000) all don't really yield the result I am looking for. The window keeps jumping further down the page.
What magical call do I need to be making to make this happen?
(PS. Using vanilla js, prefer not to introduce libraries as otherwise nothing is done in js)

Animate image left to right on scroll

What I am trying to do is I have around 6 inline images I want slide them left to right on specific position and stop there for each image. And images have to slide at the time the scrool comes over them.
I tried this javascript for it (totally new to JS)
$(window).scroll(function(){
if($this.scrollTop()>300)
{
$('.onfoot1').slideright();
}
function slideright(){
var a = getElementsByClassName('.onfoot1');
var stoppos = 100;
if (parseInt(a.style.left)< stoppos )
{
a.style.left = parseInt(a.style.left) + 3 + "px";
setTimeout(slideright , 1);
}
}
});
Markup
<div class="onfoot1"></div>
CSS
div.onfoot1{
content:url(../img/onfoot1.jpg);
left:0;
}
I've put together a working examle for your code: https://jsfiddle.net/hmzw9y65/
I've made a few assumptions there... You are using $(...) syntax so I guessed you are using JQuery. JQuery has a .animate() function which should do the trick (http://api.jquery.com/animate/). Also I guessed that you may want to make the css-position of the div fixed so it stays on screen when you scroll.
EDIT: I noticed that you don't want you image on the bottom of the screen but animating when screen reaches it. Updated my fiddle to do that: https://jsfiddle.net/hmzw9y65/1/

Creating a sliding image gallery that does not glitch on image change

I have created a sliding image gallery and when the button is pushed it slides the picture across and updates the image attribute for the relevant sections.
However this works perfectly like 50% of the time. The other times there is a second glitch and the images then go in place as expected.
I have attached the javascript methods for the animate method and the array change method. I have looked elsewhere and cannot see anyone else with a similar issue or where I am going wrong, especially when it doesn't happen often.
imageGallery.leftSelect.onclick = function () {
window.setTimeout(imageGallery.rightClick, 250);
imageGallery.animateImages('.image1', '.imageRight');
imageGallery.animateImages('.imageRight', '.imageNoneRight');
imageGallery.animateImages('.imageLeft', '.image1');
imageGallery.animateImages('.imageNoneLeft', '.imageLeft');
};
animateImages: function (classFrom, classTo) {
var classMoving = $(classFrom);
var classGoingTo = $(classTo);
classMoving.animate({
top: classGoingTo.css('top'),
left: classGoingTo.css('left'),
width: classGoingTo.css('width'),
opacity: classGoingTo.css('opacity'),
}, 258, function () {
console.log('Animated');
classMoving.css({"width":'', "opacity":'', "top":'', "left":'', });
});
},
rightClick: function () {
imageGallery.imagesDisplay.push(imageGallery.imagesDisplay.shift());
imageGallery.imageNoneLeft.setAttribute('src', imageGallery.imagesDisplay[2]);
imageGallery.imageLeft.setAttribute('src', imageGallery.imagesDisplay[1]);
imageGallery.imageMain.setAttribute('src', imageGallery.imagesDisplay[0]);
imageGallery.imageRight.setAttribute('src', imageGallery.imagesDisplay[10]);
imageGallery.imageNoneRight.setAttribute('src', imageGallery.imagesDisplay[9]);
},
Can someone assist, I really need this to work?
If there is anything not clear or you need more code let me know.
Thanks,
First things first, the culprit was the setAttribute of all images i.e. whatever you were doing inside the rightClick and leftClick functions were the reasons why you were seeing a glitch. Changing src of an img tag produces the glitch.
But then we cannot simply remove it because your approach relies heavily on this swapping of images.
I had to breakdown and really understand your approach first. The way it worked was that you would animate, for example, image1 (the centered one) to move to the position of imageLeft upon click on the rightCarousel button. On that same click, you had a setTimeout of almost the duration of the animation to call rightClick function. This rightClick function then swaps the images so that image1 can always remain at the center and only images can come and go after animation. This was the problem.
What I had to change was that all image tags i.e. imageNoneLeft, imageLeft, image1, imageRight & imageNoneRight would change each others classes such that their position remains changed after animations.
Also, I had to add another animateImages line inside your leftSelect and rightSelect callbacks to animate the furthest images i.e. imageNoneLeft & imageNoneRight to animate to each other's positions with respect to the click of the buttons.
Take a look at this jsFiddle. It will help you understand a lot better. And let me know if you have any questions.
JavaScript:
var imageGallery={
prefix:'https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/',
imagesDisplay:['JS.jpg','PIXI.jpg','GSAP.jpg','JS.jpg','PIXI.jpg','GSAP.jpg','JS.jpg','PIXI.jpg','GSAP.jpg','JS.jpg','PIXI.jpg'],
rightSelect:document.querySelector('.rightCarousel'),
leftSelect:document.querySelector('.leftCarousel'),
imageMain:document.querySelector('.image1'),
imageLeft:document.querySelector('.imageLeft'),
imageRight:document.querySelector('.imageRight'),
imageNoneLeft:document.querySelector('.imageNoneLeft'),
imageNoneRight:document.querySelector('.imageNoneRight'),
init:function(){
imageGallery.imagesDisplay.push(imageGallery.imagesDisplay.shift());
imageGallery.imageNoneLeft.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[2]);
imageGallery.imageLeft.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[1]);
imageGallery.imageMain.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[0]);
imageGallery.imageRight.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[10]);
imageGallery.imageNoneRight.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[9]);
},
animateImages:function(classFrom,classTo){
var classMoving=$(classFrom);
var classGoingTo=$(classTo);
classMoving.animate({
top:classGoingTo.css('top'),
left:classGoingTo.css('left'),
width:classGoingTo.css('width'),
opacity:classGoingTo.css('opacity')
},258,function(){
$(this).removeClass(classFrom.substr(1));
$(this).addClass(classTo.substr(1));
$(this).removeAttr('style');
});
}
};
imageGallery.init();
imageGallery.leftSelect.onclick=function(){
imageGallery.animateImages('.imageNoneRight','.imageNoneLeft');
imageGallery.animateImages('.imageRight','.imageNoneRight');
imageGallery.animateImages('.image1','.imageRight');
imageGallery.animateImages('.imageLeft','.image1');
imageGallery.animateImages('.imageNoneLeft','.imageLeft');
};
imageGallery.rightSelect.onclick=function(){
imageGallery.animateImages('.imageNoneLeft','.imageNoneRight');
imageGallery.animateImages('.imageLeft','.imageNoneLeft');
imageGallery.animateImages('.image1','.imageLeft');
imageGallery.animateImages('.imageRight','.image1');
imageGallery.animateImages('.imageNoneRight','.imageRight');
};

Mouseover/mouseout, mouseenter/mouseleave, hover flickering issue

I have a div with an image inside it. When I hover the image, I create a tooltip div thats absolutely positioned over part of the image (the absolute position is important). It contains the title and alt text.
This is all well and good until you hover the tooltip box. It doesn't bubble down and it thinks I'm no longer hovering over the image, thus making the tooltip box disapear. But then it registers I'm hovering the image again and it goes back and forth between showing the tooltip box and hiding it.
Thus the flickering issue.
There are a bunch of posts on SO about the flickering issue and I've tried so many solutions but none have been working. I've tried Mouseover/mouseout, mouseenter/mouseleave, hover, and even using live() in combination with them. I even switched from creating the tooltip from scratch to having the empty div there so it would be in the DOM when the page loaded in case that was the issue. I really don't know what to do anymore. Here is my code at the moment.
$("img").bind("mouseover", function() {
var pimg = $(this);
var position = pimg.position();
var top = position.top;
var left = position.left;
var title = $(this).attr('title');
var alt = $(this).attr('alt');
$('.toolTip').css({'left' : left, 'top' : top, 'width' : width}).append('<div><h3>' + title + '</h3><p>' + alt + '</p></div>');
});
$("img").bind("mouseout", function() {
$('.toolTip').empty();
});
The problem is a) you need to use mouseenter / mouseleave and b) the tooltip div needs to live inside the element that has the mouseenter / mouseleave listeners.
eg:
<div id="mouseoverdiv">
<div class="tooltip">some text</div>
<img src="" />
</div>

image gallery /slide with zoom

I wanted to do something similar to this.
In this case when the user click in the image, this images is showed with 100% of the browser height, and the user can go to the next/previous image. When the user clicks again the image is showed in a bigger size(may be in the real size) and the user can go up and down in the image, but with out scroll, just moving the mouse.
What I want to do is when the user click the first time in the image go right to the last step: The biggest image with up and down synchronized with the mouse movement, and the possibility to go to the next image. In other words a mix with the features of the first and the second step of the original case.
Where I can see a tutorial, or a demo?? or how can I do the this??
Thanks
Basically, there are three parts to what you want to do.
Clicking on the image will show the image with respect to browser height
You can go to the next image while you are in this mode
Click on that image again will go into a supersize mode where your mouse position dictates what part of the image you are looking at
I'm not going to write a whole fiddle to demonstrate this because it's a decent amount of work but I can tell you the basic ideas.
With #1, when you click on the image, you will create a new div with a z-index of some high number (like 9999). The position would be fixed, and you will create
$(window).resize(function() {
var windowheight = $(window).height();
$("#imgdiv").css("height", windowheight);
});
Which will resize the image if the user decides to resize your window, this way it's always taking up the full height of your browser.
With #2, the arrows just create a new img tag. And the idea is something like
function loadnew() {
// create the new image
var newimg = "<img id='newimg'></img>"
$("#imgcontainer").append(newimg);
// make sure it has the same classes as the current img
// so that it's in the same position with an higher z-index
// then load the image
$("#newimg").addClass( "class1 class2" );
$("#newimg").css( "z-index", "+=1" );
$("#newimg").css( "opacity", 0 );
$("#newimg").attr("src", "url/to/img");
// animate the thing and then replace the src of the old one with this new one
$("#newimg").animate( {
opacity: 1;
}, 1000, function() {
$(oldimg).attr("src", $("#newimg").attr("src"));
});
}
Now with #3, you will size the image with respect to the width. The div fixed positioned. So again, you need a
$(window).resize(function() {
var windowwidth= $(window).width();
$("#imgdiv").css("width", windowwidth);
});
to make sure it's always taking up the whole screen. And for the mouse movement, you need to have a mousemove event handler
$("#superimgdiv").mousemove( function(e) {
// need to tell where the mouse is with respect to the window
var height = $(window).height();
var mouseY = e.pageY;
var relativepct = mouseY/height;
// change the position relative to the mouse and the full image height
var imgheight = $("superimg").height();
$("superimgdiv").css("top", -1*relativepct*imgheight);
});
And that's it. Of course I'm leaving out a bunch of details, but this is the general idea. Hopefully this can get you started. Good luck.

Categories

Resources