13

我有一个脚本,当您将鼠标悬停在缩略图上时,它会在缩略图上动态添加完整图像。我还为完整图像提供了 CSS :hover 样式,以使它们扩展到更大的宽度(通常它们被限制为缩略图的尺寸)。如果图像加载速度很快或被缓存,这可以正常工作,但如果整个图像需要很长时间才能加载并且在加载时您不移动鼠标,那么一旦它出现,它通常会保持在缩略图宽度( non-:hover 样式),直到您再次移动鼠标。我在我尝试过的所有浏览器中都出现了这种行为。我想知道这是否是一个错误,以及是否有办法修复或解决它。

可能值得注意的是,我也尝试在 Javascript 中使用 做同样的事情.on('mouseenter'),但遇到了同样的问题。

由于问题的性质,它可能很难重现,特别是如果您有快速连接。我从 Wikipedia 中选择了一张较大的照片进行演示,但要使其正常工作,您可能必须将其更改为特别大的内容或来自慢速域的内容。另请注意,您可能必须清除缓存以进行连续重试。

如果您仍然无法重现,您可以fullimage.load在调用anchor.show().

HTML:

<img id="image" src="http://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Cairo_International_Stadium.jpg/220px-Cairo_International_Stadium.jpg" />

CSS:

.kiyuras-image {
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 220px;
}

.kiyuras-image:hover {
    max-width: 400px;
}

JS:

$(function () {

    var fullimageurl = 'http://upload.wikimedia.org/wikipedia/commons/3/32/Cairo_International_Stadium.jpg';

    var fullimage = $('<img/>')
        .addClass('kiyuras-image')
        .load(function () {
            anchor.show();
        });

    var anchor = $('<a/>').hide().append(fullimage);

    $('body').prepend(anchor);

    $("#image").on('mouseenter', function () {
        fullimage.attr('src',fullimageurl);
        $(this).off('mouseenter');
    });

});

JS斌

更新的 JS Bin 添加了 1.5 秒的延迟(希望使问题更清晰)

再次:重现问题涉及清除大图像的缓存,然后将鼠标悬停在原始图像上以初始加载大图像,然后在加载时不要移动鼠标。预期行为是让大图像在最终加载时正确呈现 :hover 伪类。我看到的问题是加载时间超过 ~0.75 秒时,它不会占用 :hover 直到您稍微晃动鼠标。

编辑:有关我的用例的更多详细信息,请参阅我对@LucaFagioli 答案的评论。

编辑,续集:我以为我已经这样做了,但我只是试图在 Firefox 中重现该问题,但我做不到。也许这是一个 Chrome 错误?

4

9 回答 9

7

大多数浏览器hover仅在光标在元素上移动至少一个像素时才更新其状态。当光标进入缩略图时img,它会被hover应用并运行您的mouseenter处理程序。如果在加载全尺寸图像之前保持光标不动,则旧img的(缩略图)将保持hover状态,而新的将不会得到它。

要让它在这些浏览器中工作,请将hover伪类移动到 CSS 中的公共父元素;例如,将两个imgs 都包含在 a 中span

于 2012-11-06T20:41:58.067 回答
2

如果选择器正确,CSS 将应用于所有元素,无论是动态的还是其他的。这包括所有的伪类,并且会随着 DOM 中的属性的变化而变化。

于 2012-10-29T11:25:22.033 回答
1

[编辑:虽然我的解释可能很有趣,但上面的 pozs 解决方案更好,所以如果可以的话,我建议使用它。]

hover伪类规范对于何时应该被激活是相当宽松的:

CSS 没有定义哪些元素可能处于上述状态,或者状态如何进入和离开。脚本可能会改变元素是否对用户事件做出反应,并且不同的设备和 UA 可能有不同的指向或激活元素的方式。

特别是,当您在加载时更新锚元素的可见性时,它不会被激活。

你可以很容易地解决这个问题:将hover样式复制到一个类,拦截光标移动到它最终将覆盖的元素上,并基于该元素从元素中添加或删除你的类。

演示: JS Bin(基于您的延迟示例)

Javascript:

$("#image")
  .on('mouseenter', function () {
    fullimage.attr('src',fullimageurl).toggleClass('mouseover', true);
    $(this).off('mouseenter');
  })
  .mouseleave(function() {
    fullimage.toggleClass('mouseover', false);
  });

CSS:

.kiyuras-image:hover, .kiyuras-image.mouseover {
    max-width: 400px;
}
于 2012-11-06T15:08:57.233 回答
1

TL;DR:您不能依赖于:hover应用到光标下动态添加的元素。但是,纯 CSS 和 Javascript 都有可用的解决方法。

我赞成 Jordan Gray 和 posz 的回答,我希望我能给他们两个赏金。Jordan Gray 以某种决定性的方式解决了这个问题:CSS 规范,并提供了(另一个)工作修复,它仍然允许 :hover 和其他 CSS 效果,如转换,除了加载。posz 提供了一个更好的解决方案,并且避免了任何悬停事件的 Javascript;我在这里提供了基本相同的解决方案,但使用 div 而不是 span。我决定把它授予他,但我认为乔丹的投入是必不可少的。我正在添加并接受我自己的答案,因为我觉得有必要自己详细说明所有这些。(编辑:改变,我接受了 posz')

Jordan 引用了 CSS2 规范;我将改为参考 CSS3。据我所知,他们在这一点上没有区别。

有问题的伪类是 :hover,它指的是用户“用指针设备指定”的元素。为了允许不同类型的交互和媒体,故意模糊了行为的确切定义,不幸的是,这意味着规范没有解决以下问题:“是否应该在指针设备下出现的新元素应用这个伪类? " 这是一个很难回答的问题。在大多数情况下,哪个答案与用户意图一致?用户正在与之交互的页面的动态变化通常是正在进行的用户交互或为相同的准备做准备的结果。因此,我会说是的,并且大多数当前的浏览器似乎都同意。通常,当您在光标下添加元素时,会立即应用 :hover。你可以在这里看到这个:我最初发布的jsbin。请注意,如果加载较大的图像有延迟,您可能需要刷新页面才能使其正常工作,原因我将详细说明。

现在,有一个类似的情况,用户通过 :hover 规则将光标固定在一个元素上而激活浏览器本身;它应该适用于那种情况吗?在这种情况下,鼠标“悬停”不是直接用户交互的结果。但是指点设备正在指定它,对吗?此外,鼠标的任何移动都会产生明确的交互。这是一个更难回答的问题,浏览器以不同的方式回答它。当您激活它们时,Chrome 和 Firefox 不会更改 :hover 状态,直到您移动鼠标(即使您通过单击激活它们!)。另一方面,Internet Explorer 会在激活 :hover 状态后立即更新。事实上,即使它不活动,它也会更新它,只要它是鼠标下的第一个可见窗口。

不过,让我们回到第一个案例,因为这是我当前的问题出现的地方。在我的例子中,用户在很长一段时间内(超过一秒)没有移动鼠标,并且直接在光标下方添加了一个元素。这可能更容易被认为是用户交互不明确且不应切换伪类的情况。个人认为还是应该适用的。但是,大多数浏览器似乎并不同意我的看法。当您第一次将鼠标悬停在图像上然后不要在此 jsbin中移动鼠标时(这是我在问题中发布的用于演示问题的问题,并且与第一个问题一样,有一个简单的 :hover 选择器), :hover 类不是适用于当前的 Chrome、Opera 和 IE。(Safari 也不会应用它,但有趣的是,如果您继续按键盘上的某个键,它会应用它。)然而,在 Firefox 中,立即应用 :hover 类。由于 Chrome 和 Firefox 是我最初测试的仅有的两个,我认为这是 Chrome 中的一个错误。但是,规范在这一点上或多或少完全保持沉默。大多数实现都说不。Firefox 和我说是的。

以下是规范的相关部分:

:hover 伪类在用户使用指针设备指定元素时应用,但不一定激活它。例如,当光标(鼠标指针)悬停在元素生成的框上时,可视用户代理可以应用此伪类。不支持交互式媒体的用户代理不必支持这个伪类。一些支持交互式媒体的符合标准的用户代理可能无法支持这个伪类(例如,不检测悬停的笔设备)。

[...]

选择器没有定义 ':active' 或 ':hover' 元素的父元素是否也处于该状态。

[...]

注意:如果 ':hover' 状态适用于一个元素,因为它的子元素是由指针设备指定的,那么 ':hover' 可能适用于不在指针设备下方的元素。

所以!解决方法!正如一些人在这个线程中热心地指出的那样,Javascript 和 jQuery 也为此提供了解决方案,它们依赖于 'mouseover' 和 'mouseenter' DOM 事件。在问这个问题之前和之后,我自己探索了很多这样的解决方案。然而,这些都有自己的问题,它们的行为略有不同,而且它们通常只涉及简单地切换 CSS 类。此外,如果没有必要,为什么要使用 Javascript?

我有兴趣找到一个使用 :hover 的解决方案,这就是它 (jsbin)。我们没有将 :hover 放在要添加的元素上,而是将其放在包含该新元素的现有元素上,并且占用相同的物理空间;在这种情况下,一个 div 包含缩略图和新的较大图像(当不悬停时,它将与 div 和缩略图大小相同)。这似乎对我的用例相当具体,但通常可以使用与新元素大小相同的定位 div 来完成。

补充:我写完这个答案后, pozs提供了与上面基本相同的解决方案!

这与完整的 Javascript 解决方案之一之间的折衷方案是拥有一个一次性使用的类,该类将在添加新元素时有效地依赖 Javascript/DOM 悬停事件,然后删除所有这些并依赖 :hover 前进. 这是 Jordan Gray 提供的解决方案(Jsbin)

这两种浏览器都适用于我尝试过的所有浏览器:Chrome、Firefox、Opera、Safari 和 Internet Explorer。

于 2012-11-06T21:05:51.747 回答
0

从您问题的这一部分开始:“如果图像加载速度很快或被缓存,这可以正常工作,但是如果完整图像需要很长时间才能加载并且您在加载时不移动鼠标,”

首先用 JavaScript “预加载”所有图像是否值得。这可能会允许所有图像首先成功加载,并且对于连接速度较慢的人可能会更加用户友好。

于 2012-10-29T11:30:39.570 回答
0

你可以这样做:http: //jsfiddle.net/jR5Ba/5/

总之,在你的图片前面附加一个加载布局,然后附加一个包含你的大图片的 div 和一个.load()回调来删除你的加载层。

上面的fiddle由于时间关系没有简化和清理,但是如果需要的话我明天可以继续工作。

$imageContainer = $("#image-container");    
$image = $('#image');

$imageContainer.on({
    mouseenter: function (event) {    
       //Add a loading class
       $imageContainer.addClass('loading');
       $image.css('opacity',0.5); 

       //Insert div (for styling) containing large image            
       $(this).append('<div><img class="hidden large-image-container" id="'+this.id+'-large" src="'+fullimageurl+'" /></div>');

       //Append large image load callback            
       $('#'+this.id+'-large').load(function() {
           $imageContainer.removeClass('loading');
           $image.css('opacity',1);
           $(this).slideDown('slow');
           //alert ("The image has loaded!");        
       });
    },            
    mouseleave: function (event) {
       //Remove loading class
       $imageContainer.removeClass('loading');
       //Remove div with large image 
       $('#'+this.id+'-large').remove();
       $image.css('opacity',1);             
    }        
});

编辑

这是一个新版本的小提琴,包括正确大小的加载层和显示大图片时的动画:http: //jsfiddle.net/jR5Ba/6/

希望它会有所帮助

于 2012-10-31T20:54:46.387 回答
0

在有要下载的图像之前,不要让 IMG 标记添加到 DOM。这样,在加载图像之前,Load 事件不会触发。这是修改后的JS:

$(function () {

    var fullimageurl = 'http://upload.wikimedia.org/wikipedia/commons/3/32/Cairo_International_Stadium.jpg';

    var fullimage = $('<img/>')
        .addClass('kiyuras-image')
        .load(function () {
            anchor.show(); // Only happens after IMG src has loaded
        });

    var anchor = $('<a/>').hide();

    $('body').prepend(anchor);

    $("#image").on('mouseenter', function () {
        fullimage.attr('src',fullimageurl); // IMG has source
        $(this).off('mouseenter');
        anchor.append(fullimage); // Append IMG to DOM now.
    });

});
于 2012-11-02T22:52:59.027 回答
0

我不是 100% 确定为什么:hover只有轻微的鼠标移动才会触发声明。一个可能的原因可能是从技术上讲,您可能没有真正悬停该元素。基本上,您在加载时将元素推到光标下(直到大图像完全加载该A元素具有display: none并且因此不可能处于该:hover状态)。同时,这并不能解释较小图像的差异...

因此,一种解决方法是只使用 JavaScript 并将:hover语句排除在等式之外。只需IMG根据悬停状态(在 JavaScript 中切换)向用户显示两个不同的元素。作为一个额外的优势,图像不必由浏览器动态放大和缩小(Chrome 中的视觉故障)。

http://jsbin.com/ifitep/34/

更新:通过使用 JavaScript.active在大图像上添加一个类,完全可以继续使用原生 CSS 动画。见http://jsbin.com/ifitep/48

于 2012-11-05T18:57:13.300 回答
0

我这样做了,它在 Chrome(版本 22.0.1229.94 m)上运行:我将 css 更改为:

.kiyuras-image{
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 400px;
}
.not-hovered{
    max-width: 220px;
}

和这样的脚本:

$(function(){
    var fullimageurl = 'http://upload.wikimedia.org/wikipedia/commons/3/32/Cairo_International_Stadium.jpg';

    var fullimage = $('<img/>')
        .addClass('kiyuras-image')
        .load(function () {
            anchor.show();
        });

    var anchor = $('<a/>').hide().append(fullimage);

    $('body').prepend(anchor);

    $('.kiyuras-image').on('mouseout',function(){
        $(this).addClass('not-hovered');
    });
    $('.kiyuras-image').on('mouseover',function(){
        $(this).removeClass('not-hovered');
    });

    $("#image").one('mouseover', function(){
        fullimage.attr('src',fullimageurl);
    });
});

基本上我认为这是检测/呈现“悬停”状态的 Chrome 错误;事实上,当我试图简单地将 CSS 更改为:

.kiyuras-image{
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 400px;
}
.kiyuras-image:not(:hover) {
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 220px;
}

它仍然没有用。

PS:对不起我的英语。

于 2012-11-06T15:59:56.030 回答