问题 如何确保CSS:hover应用于动态添加的元素


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

值得注意的是,我也尝试在Javascript中使用相同的功能 .on('mouseenter'),并遇到了同样的问题。

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

如果仍然无法重现,可以添加一个人工延迟 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 Bin

更新了JS Bin,增加了1.5秒延迟(希望问题更清晰)

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

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

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


1148
2017-10-29 11:23


起源

只是注意:不是使用.on()然后再使用.off()解除绑定,而是使用.one()。看到 api.jquery.com/one - xec
@xec,酷,谢谢你的提示! - jrajav
设置一个jsfiddle,人们会更清楚地看到你的问题 - Mild Fuzz
@MildFuzz,最后有一个JS Bin链接。对不起,我知道这可能会更加引人注目。 - jrajav
只是fyi,那个 .delay(1000) 并没有延迟任何事情,因为它只会延迟动画。 .delay(1000).show(0) 会做的伎俩 - antishok


答案:


大多数浏览器更新他们 hover 仅当光标在元素上移动至少一个像素时才处于状态。当光标进入缩略图时 img 它得到了 hover 申请并运行你的 mouseenter 处理程序。如果你将光标保持不动,直到加载完整尺寸的图像,那么你的光标就是旧的 img (缩略图)将保留 hover 国家和新的不会得到它。

要让它在这些浏览器中运行,请移动 hover 伪类到CSS中的公共父元素;例如, 两者都包括在内 img在一个 span


7
2017-11-06 20:41



在考虑了一些之后,接受我自己的答案并不是很公平 - 这是解决方案的重要部分,而我的巨大解释并没有增加太多。基本上,浏览器的工作方式,这是实现此效果的可靠方法。如果您想知道为什么会出现这种情况,请参阅下面的答案。 - jrajav
这是一个非常简洁的解决方案,BTW。比我的好,所以我更新了我的答案指向这个。 - Jordan Gray
此解决方案不允许任何动画。如今在专业环境中使用它是不可想象的。看到我的回答。 - Luca Fagioli
不我不是。任何版本的IE都不支持CSS3过渡属性。您可以使用相同的链接进行验证。 - Luca Fagioli


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


2
2017-10-29 11:25



什么是“指针”? - xec
他们当然应该这样,我同意。然而,这并不是我所看到的,正如我试图详细解释的那样。你能重现我的问题吗? - jrajav
你的意思是选择对吗? - Pranav 웃
固定为学生;) - Mild Fuzz
他们只是言语,老兄。只有言语。 ;) - Mild Fuzz


[编辑:虽然我的解释可能会引起关注, 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;
}

1
2017-11-06 15:08





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

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

Jordan引用了CSS2规范;我会转而使用CSS3。据我所知,他们在这一点上没有区别。

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

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

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

以下是相关部分 规范

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

[...]

选择器不定义“:active”或“:hover”元素的父级是否也处于该状态。

[...]

注意:如果':hover'状态适用于元素,因为它的子节点由指针设备指定,则':hover'可以应用于不在指针设备下面的元素。

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

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

添加:在我完成这个答案后,pozs提供了与上面基本相同的解决方案!

这个和一个完整的Javascript解决方案之间的妥协是有一个一次性使用的类,它将有效地依赖Javascript / DOM悬停事件,同时添加新元素,然后删除所有这些并依赖于:hover继续。这是Jordan Gray提供的解决方案 (Jsbin)

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


1
2017-11-06 21:05





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

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


0
2017-10-29 11:30



是的,我想到了,但是这个脚本打算用在可能有很多这些图像的页面上,即使加载是异步完成的,加载几兆字节的图像也可能是一个不受欢迎的带宽消耗可能无法使用。 - jrajav


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

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

由于时间不够,上面的小提琴没有被简化和清理,但如果需要,我可以继续在明天继续工作。

$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/

希望它会有所帮助


0
2017-10-31 20:54



这个答案也没有直接解决我的问题。实际上,它对图像本身没有任何作用:悬停甚至是.on('mouseenter')。此外,您的解决方案会导致大图像在“大”尺寸下明显加载,这并不理想。它也隐藏了这个问题,因为我的问题涉及在图像加载完成后将:hover或.on('mouseenter')触发以将其调整为大尺寸;与你的一起,这是不需要的,因为它已经达到了那么大。 - jrajav
你是对的,这不是一个惊人的小提琴。我用另一个更新了我的答案,我希望这能帮助您找到有关您问题的解决方案 - sdespont
好吧,那个也不太理想,因为它导致动画从大小0开始而不是原始图像的大小。此外,即使已加载大图像,加载动画也会排队。不过,这些都是细节;真正的问题是你是如何解决我的主要问题的,答案似乎是你每次都完全添加和删除大图像,而是依赖于原始缩略图的鼠标中心。我不希望在性能和视觉一致性方面走这条路,但值得一试。最后,它仍然是一种解决方法。 - jrajav
大图像动画不是问题,使用JQuery存在很多可能性,还有更多使用UI扩展(jqueryui.com/effect)或者根本就没有动画。我确信CSS:hover不能用于知道图像是否完全加载,这就是为什么我使用这个技巧创建一个动态div来显示它。使用这个新的div,可以添加加载行为,以使用户可以理解大图像加载。如果您不喜欢这个,我没有任何其他想法,否则我可以帮助您调整您的需求 - sdespont
我花时间思考并写下这个答案,请花时间评论你的downvote! - sdespont


在有要下载的映像之前,不要让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.
    });

});

0
2017-11-02 22:52



我仍然看到这个JS的问题。另外,如果我没有弄错的话,在具有src属性之前,图像甚至不会被开始检索 和 它被添加到DOM?顺便说一下,我可能会问一个无法回答的问题 - 这可能是一个浏览器错误。抱歉!我很快就会发现。如果是的话,我会发一个自我回答。 - jrajav
正确,直到IMG标记具有src属性后才开始检索图像。但是,您的原始代码在拥有src之前将IMG标记添加到DOM。因此,它在检索图像之前触发了load事件。通过重新排序指令序列,加载事件(以及您所在的语句) 显示() 检索图像后发生锚点)。这在我的Chrome副本中完美运行。 - joequincy
你能定义“完美的作品吗?”你说你可以用我发布的原始jsbin重现我的问题 - 也就是说,加载页面后第一次鼠标悬停,然后不移动鼠标并等待大约2秒,导致:悬停不适用和更大图像保留在缩略图宽度 - 但使用JS替换的相同复制步骤(版本12 / jsbin)导致:悬停应用?因为那不是我所看到的。 - jrajav
好吧,除了你正在使用的图像几乎立即加载,所以为了重现你在问题中提到的滞后效应更自然,我使用 一个更大的图像。图像在JSBin中以较小的尺寸显示(尽管它在显示后大约在一秒钟内将其自身校正到400px宽度)。然而,在我的编辑中,当图像被下载时,它会出现并立即跳转到400px大小。我使用的是Chrome,没有扩展名。 - joequincy


我做到了,它适用于Chrome(版本22.0.1229.94米): 我改变了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:对不起我的英语。


0
2017-11-06 15:59





我不是百分百肯定为什么 :hover 声明仅在轻微鼠标移动时触发。一个可能的原因可能是技术上你可能不是真的 徘徊 元素。基本上你是在加载光标时推动光标下的元素(直到大图像被完全加载 A 元素有 display: none 因此不可能在 :hover 州)。与此同时,这并不能解释与较小图像的差异......

所以,一个解决方法是只使用JavaScript并离开 :hover 声明之外的声明。只是向用户展示两种不同的用户 IMG 元素取决于悬停状态(在JavaScript中切换)。作为额外的优势,图像不必由浏览器动态地放大和缩小(Chrome中的视觉故障)。

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

UPDATE:通过使用JavaScript添加 .active 在大图像上,完全可以继续使用原生CSS动画。看到 http://jsbin.com/ifitep/48


0
2017-11-05 18:57



很好的洞察力,但走这条路线不允许它动画。但是,如果没有动画,那么在两个图像之间切换比缩放更大的图像要好。感谢您的回答! - jrajav
实际上,通过使用JavaScript来添加类,实际上需要使用:hover。看到 jsbin.com/ifitep/48 要么 jsbin.com/ifitep/49 这增加了缩放效果(天空是极限!)。 - Joram van den Boezem