Gallery Magnifier hover event does not always cancel magnifier when mouse leaves gallery
<var name="magnifier">
<var name="fullscreenzoom">20</var> <!-- Zoom for fullscreen (integer)-->
<var name="top"></var> <!-- Top position of magnifier -->
<var name="left"></var> <!-- Left position of magnifier -->
<var name="width"></var> <!-- Width of magnifier block -->
<var name="height"></var> <!-- Height of magnifier block -->
<var name="eventType">hover</var> <!-- Action that atcivates zoom (hover/click) -->
<var name="enabled">true</var> <!-- Turn on/off magnifier (true/false) -->
</var>
I have the same issue (with and without your PR applied).
It seems to have to do with the following PR #13084.
When reverting the change made in the PR it works fine.
The solution at the bottom of the page mentioned in the original issue https://github.com/magento/magento2/issues/5129#issuecomment-381663605 also doesnt fix this issue.
Yeah, confirm, works fine if you revert that change.
Having same issue with Magento 2.2.4.
@dmanners Can you please assign me this issue? Or add me in the contribution team so that I can self-assign issues to myself. As I have solution for this issue, I need to commit the code and generate pull request for the same.
Same issue with word "image" under the photo:

<div class="fotorama__caption__wrap" id="labelledby1527757266301">Image</div>
For a quick solution you can follow https://magento.stackexchange.com/questions/227354/product-image-zoom-issue-on-product-details-page-when-drop-down-menu-have-overla/227398#227398 this.
@valanitin
Seems that will solve with Gallery Magnifier hover. Thanks. And what's about second part of issue: the word "image" under the photo?
See #15020 for fix for label under image
Hello @gwharton, thank you for your report.
We've acknowledged the issue and added to our backlog.
I would like to add a little more insight into this; I think it may be the same issue. When there is more than one product image, and slides are enabled (i.e. such that a left and right arrow is overlaid onto the product image on mouse over), if the magnifier cursor exits the product image via the overlaid arrows, the zoom window gets stuck on as described in the original post. This only appears to happen when the product image ratio is landscape, i.e. such that part of the image is behind the overlaid arrows.
Preconditions
I noticed this on a Magento 2.2.5 installation with Ultimo theme installed. I then recreated it using a clean installation PHP 7.1, Ubuntu 16.04; mysql 5.7; using composer; Magento CE 2.2.5 install, Lumia theme set
Steps to reproduce
(modified view.xml as above to enable magnifier) and created a product with a variety of landscape and portrait images.
Expected
On main product view, zoomed in preview window should close on cursor exiting the area enclosing the product image and left / right image scrolling arrows)
Actual
When cursor exits via left/right (on landscape image) the preview window stays open.
This is still broken in 2.2.6, but now reverting the change made in #13084 no longer fixes this issue.
+1, still happens in 2.2.6
Today, we are happy to show you how to fix Magento 2 issue: Magnifier not working for product image on product details page. It's showing overlay image after hovering over product image but it's not zooming the image.
Luckily, the solution to fix this is quite easy. You can apply the following way to fix it now.
Please go to lib\web\magnifier\magnifier.js and replace it by the following code:
Magento 2 Zoom Issue
/**
* Copyright 漏 Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
(function($) {
$.fn.magnify = function(options) {
'use strict';
var magnify = new Magnify($(this), options);
/*events must be tracked here*/
/**
* Return that from _init function
*
*/
return magnify;
};
function Magnify(element, options) {
var gOptions = options || {},
$box = $(element),
$thumb,
that = this,
largeWrapper = options.largeWrapper || '.magnifier-preview',
$largeWrapper = $(largeWrapper);
curThumb = null,
currentOpts = {
x: 0,
y: 0,
w: 0,
h: 0,
lensW: 0,
lensH: 0,
lensBgX: 0,
lensBgY: 0,
largeW: 0,
largeH: 0,
largeL: 0,
largeT: 0,
zoom: 2,
zoomMin: 1.1,
zoomMax: 5,
mode: 'outside',
eventType: 'click',
status: 0,
zoomAttached: false,
zoomable: gOptions.zoomable !== undefined ?
gOptions.zoomable :
false,
onthumbenter: gOptions.onthumbenter !== undefined ?
gOptions.onthumbenter :
null,
onthumbmove: gOptions.onthumbmove !== undefined ?
gOptions.onthumbmove :
null,
onthumbleave: gOptions.onthumbleave !== undefined ?
gOptions.onthumbleave :
null,
onzoom: gOptions.onzoom !== undefined ?
gOptions.onzoom :
null
},
pos = {
t: 0,
l: 0,
x: 0,
y: 0
},
gId = 0,
status = 0,
curIdx = '',
curLens = null,
curLarge = null,
lensbg = gOptions.bg !== undefined ? gOptions.lensbg : true,
gZoom = gOptions.zoom !== undefined ?
gOptions.zoom :
currentOpts.zoom,
gZoomMin = gOptions.zoomMin !== undefined ?
gOptions.zoomMin :
currentOpts.zoomMin,
gZoomMax = gOptions.zoomMax !== undefined ?
gOptions.zoomMax :
currentOpts.zoomMax,
gMode = gOptions.mode || currentOpts.mode,
gEventType = gOptions.eventType || currentOpts.eventType,
data = {},
inBounds = false,
isOverThumb = false,
rate = 1,
paddingX = 0,
paddingY = 0,
enabled = true,
showWrapper = true;
var MagnifyCls = {
magnifyHidden: 'magnify-hidden',
magnifyOpaque: 'magnify-opaque',
magnifyFull: 'magnify-fullimage'
};
/**
* Update Lens positon on.
*
*/
that.update = function() {
updateLensOnLoad();
};
/**
* Init new Magnifier
*
*/
that.init = function() {
_init($box, options);
};
function _toBoolean(str) {
if (typeof str === 'string') {
if (str === 'true') {
return true;
} else if (str === 'false' || '') {
return false;
}
console.warn('Wrong type: can\'t be transformed to Boolean');
} else if (typeof str === 'boolean') {
return str;
}
}
function createLens(thumb) {
if ($(thumb).siblings('.magnify-lens').length) {
return false;
}
var lens = $('
');
$(thumb).parent().append(lens);
}
function updateLensOnLoad(idx, thumb, large, largeWrapper) {
var lens = $box.find('.magnify-lens'),
textWrapper;
if (data[idx].status === 1) {
textWrapper = $('
');
lens.className = 'magnifier-loader magnify-hidden'; textWrapper.html('Loading...'); lens.html('').append(textWrapper);
}
else if (data[idx].status === 2) {
lens.addClass(MagnifyCls.magnifyHidden);
lens.html('');
large.id = idx + '-large';
large.style.width = data[idx].largeW * rate + 'px';
large.style.height = data[idx].largeH + 'px';
large.className = 'magnifier-large magnify-hidden';
if (data[idx].mode === 'inside') {
lens.append(large);
} else {
largeWrapper.html('').append(large);
}
}
data[idx].lensH = data[idx].lensH > $thumb.height() ? $thumb.height() : data[idx].lensH;
if (Math.round(data[idx].lensW) === 0) {
lens.css('display', 'none');
} else {
lens.css({
width: data[idx].lensW + 1 + 'px',
height: data[idx].lensH - 1 + 'px',
display: ''
});
}
}
function getMousePos() {
var xPos = pos.x - currentOpts.x,
yPos = pos.y - currentOpts.y,
t,
l;
inBounds = xPos < 0 || yPos < 0 || xPos > currentOpts.w || yPos > currentOpts.h ? false : true;
l = xPos - currentOpts.lensW / 2;
t = yPos - currentOpts.lensH / 2;
if (currentOpts.mode !== 'inside') {
if (xPos < currentOpts.lensW / 2) {
l = 0;
}
if (yPos < currentOpts.lensH / 2) {
t = 0;
}
if (xPos - currentOpts.w + Math.ceil(currentOpts.lensW / 2) > 0) {
l = currentOpts.w - Math.ceil(currentOpts.lensW + 2);
}
if (yPos - currentOpts.h + Math.ceil(currentOpts.lensH / 2) > 0) {
t = currentOpts.h - Math.ceil(currentOpts.lensH);
}
pos.l = l;
pos.t = t;
currentOpts.lensBgX = pos.l;
currentOpts.lensBgY = pos.t;
if (currentOpts.mode === 'inside') {
currentOpts.largeL = xPos * (currentOpts.zoom - currentOpts.lensW / currentOpts.w);
currentOpts.largeT = yPos * (currentOpts.zoom - currentOpts.lensH / currentOpts.h);
} else {
currentOpts.largeL = currentOpts.lensBgX * currentOpts.zoom * (currentOpts.largeWrapperW / currentOpts.w) * rate;
currentOpts.largeT = currentOpts.lensBgY * currentOpts.zoom * (currentOpts.largeWrapperH / currentOpts.h);
}
}
}
function onThumbEnter() {
if (_toBoolean(enabled)) {
currentOpts = data[curIdx];
curLens = $box.find('.magnify-lens');
if (currentOpts.status === 2) {
curLens.removeClass(MagnifyCls.magnifyOpaque);
curLarge = $('#' + curIdx + '-large');
curLarge.removeClass(MagnifyCls.magnifyHidden);
} else if (currentOpts.status === 1) {
curLens.className = 'magnifier-loader';
}
}
}
function onThumbLeave() {
if (currentOpts.status > 0) {
var handler = currentOpts.onthumbleave;
if (handler !== null) {
handler({
thumb: curThumb,
lens: curLens,
large: curLarge,
x: pos.x,
y: pos.y
});
}
if (!curLens.hasClass(MagnifyCls.magnifyHidden)) {
curLens.addClass(MagnifyCls.magnifyHidden);
//$curThumb.removeClass(MagnifyCls.magnifyOpaque);
if (curLarge !== null) {
curLarge.addClass(MagnifyCls.magnifyHidden);
}
}
}
}
function move() {
if (_toBoolean(enabled)) {
if (status !== currentOpts.status) {
onThumbEnter();
}
if (currentOpts.status > 0) {
curThumb.className = currentOpts.thumbCssClass + ' magnify-opaque';
if (currentOpts.status === 1) {
curLens.className = 'magnifier-loader';
} else if (currentOpts.status === 2) {
curLens.removeClass(MagnifyCls.magnifyHidden);
curLarge.removeClass(MagnifyCls.magnifyHidden);
curLarge.css({
left: '-' + currentOpts.largeL + 'px',
top: '-' + currentOpts.largeT + 'px'
});
}
pos.t = pos.t <= 0 ? 0 : pos.t;
curLens.css({
left: pos.l + paddingX + 'px',
top: pos.t + 1 + paddingY + 'px'
});
if (lensbg) {
curLens.css({
'background-color': 'rgba(f,f,f,.5)'
});
} else {
curLens.get(0).style.backgroundPosition = '-' +
currentOpts.lensBgX + 'px -' +
currentOpts.lensBgY + 'px';
}
var handler = currentOpts.onthumbmove;
if (handler !== null) {
handler({
thumb: curThumb,
lens: curLens,
large: curLarge,
x: pos.x,
y: pos.y
});
}
}
status = currentOpts.status;
}
}
function setThumbData(thumb, thumbData) {
var thumbBounds = thumb.getBoundingClientRect(),
w = 0,
h = 0;
thumbData.x = thumbBounds.left;
thumbData.y = thumbBounds.top;
thumbData.w = thumbBounds.right - thumbData.x;
thumbData.h = thumbBounds.bottom - thumbData.y;
if (thumbData.mode === 'inside') {
w = thumbData.w;
h = thumbData.h;
} else {
w = thumbData.largeWrapperW;
h = thumbData.largeWrapperH;
}
thumbData.largeW = thumbData.zoom * w;
thumbData.largeH = thumbData.zoom * h;
thumbData.lensW = thumbData.w / thumbData.zoom / rate;
thumbData.lensH = thumbData.h / thumbData.zoom;
}
function _init($box, options) {
var opts = {};
if (options.thumb === undefined) {
return false;
}
$thumb = $box.find(options.thumb);
if ($thumb.length) {
for (var key in options) {
opts[key] = options[key];
}
opts.thumb = $thumb;
enabled = opts.enabled;
if (_toBoolean(enabled)) {
$largeWrapper.show().css('display', '');
$largeWrapper.addClass(MagnifyCls.magnifyHidden);
set(opts);
} else {
$largeWrapper.empty().hide();
}
}
return that;
}
function hoverEvents(thumb) {
$(thumb).on('mouseover', function(e) {
if (showWrapper) {
if (currentOpts.status !== 0) {
onThumbLeave();
}
handleEvents(e);
isOverThumb = inBounds;
}
}).trigger('mouseover');
}
function clickEvents(thumb) {
$(thumb).on('click', function(e) {
if (showWrapper) {
if (!isOverThumb) {
if (currentOpts.status !== 0) {
onThumbLeave();
}
handleEvents(e);
isOverThumb = true;
}
}
});
}
function bindEvents(eType, thumb) {
switch (eType) {
case 'hover':
hoverEvents(thumb);
break;
case 'click':
clickEvents(thumb);
break;
}
}
function handleEvents(e) {
var src = e.target;
curIdx = src.id;
curThumb = src;
onThumbEnter(src);
setThumbData(curThumb, currentOpts);
pos.x = e.clientX;
pos.y = e.clientY;
getMousePos();
move();
var handler = currentOpts.onthumbenter;
if (handler !== null) {
handler({
thumb: curThumb,
lens: curLens,
large: curLarge,
x: pos.x,
y: pos.y
});
}
}
function set(options) {
if (data[options.thumb.id] !== undefined) {
curThumb = options.thumb;
return false;
}
var thumbObj = new Image(),
largeObj = new Image(),
$thumb = options.thumb,
thumb = $thumb.get(0),
idx = thumb.id,
largeUrl,
largeWrapper = $(options.largeWrapper),
zoom = options.zoom || thumb.getAttribute('data-zoom') || gZoom,
zoomMin = options.zoomMin || gZoomMin,
zoomMax = options.zoomMax || gZoomMax,
mode = options.mode || thumb.getAttribute('data-mode') || gMode,
eventType = options.eventType || thumb.getAttribute('data-eventType') || gEventType,
onthumbenter = options.onthumbenter !== undefined ?
options.onthumbenter :
currentOpts.onthumbenter,
onthumbleave = options.onthumbleave !== undefined ?
options.onthumbleave :
currentOpts.onthumbleave,
onthumbmove = options.onthumbmove !== undefined ?
options.onthumbmove :
currentOpts.onthumbmove;
largeUrl = $thumb.data('original') || gOptions.full || $thumb.attr('src');
if (thumb.id === '') {
idx = thumb.id = 'magnifier-item-' + gId;
gId += 1;
}
createLens(thumb, idx);
if (options.width) {
largeWrapper.width(options.width);
}
if (options.height) {
largeWrapper.height(options.height);
}
if (options.top) {
if (typeof options.top == 'function') {
var top = options.top() + 'px';
} else {
var top = options.top + 'px';
}
if (largeWrapper.length) {
largeWrapper[0].style.top = top.replace('%px', '%');
}
}
if (options.left) {
if (typeof options.left == 'function') {
var left = options.left() + 'px';
} else {
var left = options.left + 'px';
}
if (largeWrapper.length) {
largeWrapper[0].style.left = left.replace('%px', '%');
}
}
data[idx] = {
zoom: zoom,
zoomMin: zoomMin,
zoomMax: zoomMax,
mode: mode,
eventType: eventType,
thumbCssClass: thumb.className,
zoomAttached: false,
status: 0,
largeUrl: largeUrl,
largeWrapperId: mode === 'outside' ? largeWrapper.attr('id') : null,
largeWrapperW: mode === 'outside' ? largeWrapper.width() : null,
largeWrapperH: mode === 'outside' ? largeWrapper.height() : null,
onthumbenter: onthumbenter,
onthumbleave: onthumbleave,
onthumbmove: onthumbmove
};
rate = $thumb.width() / $thumb.height() / (data[idx].largeWrapperW / data[idx].largeWrapperH);
paddingX = ($thumb.parent().width() - $thumb.width()) / 2;
paddingY = ($thumb.parent().height() - $thumb.height()) / 2;
showWrapper = false;
$(thumbObj).on('load', function() {
data[idx].status = 1;
$(largeObj).on('load', function() {
if (largeObj.width > largeWrapper.width() || largeObj.height > largeWrapper.height()) {
showWrapper = true;
bindEvents(eventType, thumb);
data[idx].status = 2;
data[idx].zoom = largeObj.height / largeWrapper.height();
setThumbData(thumb, data[idx]);
updateLensOnLoad(idx, thumb, largeObj, largeWrapper);
}
});
largeObj.src = data[idx].largeUrl;
});
thumbObj.src = thumb.src;
}
function onMousemove(e) {
pos.x = e.clientX;
pos.y = e.clientY;
getMousePos();
if (gEventType === 'hover') {
isOverThumb = inBounds;
}
if (inBounds && isOverThumb) {
$largeWrapper.removeClass(MagnifyCls.magnifyHidden);
move();
} else {
onThumbLeave();
isOverThumb = false;
$largeWrapper.addClass(MagnifyCls.magnifyHidden);
}
}
function onScroll() {
if (curThumb !== null) {
setThumbData(curThumb, currentOpts);
}
}
function onMouseLeave(e) {
onThumbLeave();
isOverThumb = false;
$largeWrapper.addClass(MagnifyCls.magnifyHidden);
}
$(window).on('scroll', onScroll);
$(window).resize(function() {
_init($box, gOptions);
});
$box.on('mousemove', onMousemove);
$box.on('mouseleave', onMouseLeave);
_init($box, gOptions);
}
}(jQuery));
You can view and download these code here
Once you finished, please try to run SSH command:
php bin/magento setup:upgrade
php bin/magento setup:static-content:deploy -f
php bin/magento cache:flush
This is already fixed in 2.2-develop
See https://github.com/magento/magento2/commit/c55480ef23b6eb9902053a59d421c29c5eeebc4f#diff-a3d63022f922ef08dd75e31dddb0025a
PR for 2.3-develop here #18702
Hi @gwharton. Thank you for your report.
The issue has been fixed in magento/magento2#18702 by @gwharton in 2.3-develop branch
Related commit(s):
The fix will be available with the upcoming 2.3.1 release.
I am on 2.2.6 and still having this issue
Hi @TomashKhamlai. Thank you for working on this issue.
Looks like this issue is already verified and confirmed. But if your want to validate it one more time, please, go though the following instruction:
Component: XXXXX label(s) to the ticket, indicating the components it may be related to.[x] 2. Verify that the issue is reproducible on 2.3-develop branchDetails
- Add the comment @magento-engcom-team give me 2.3-develop instance to deploy test instance on Magento infrastructure.
- If the issue is reproducible on 2.3-develop branch, please, add the label Reproduced on 2.3.x.
- If the issue is not reproducible, add your comment that issue is not reproducible and close the issue and _stop verification process here_!
[ ] 3. Verify that the issue is reproducible on 2.2-develop branch. Details
- Add the comment @magento-engcom-team give me 2.2-develop instance to deploy test instance on Magento infrastructure.
- If the issue is reproducible on 2.2-develop branch, please add the label Reproduced on 2.2.x
[ ] 4. If the issue is not relevant or is not reproducible any more, feel free to close it.
Fixed in 2.3.1 (2.3-develop)
Hi @gwharton , thank you for you report, this issue has already fixed in 2.3-develop branch, and will be available on 2.3.1 release.
Most helpful comment
+1, still happens in 2.2.6