css 当中如何实现一个元素的hover, focus 状态改变其他元素的样式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了css 当中如何实现一个元素的hover, focus 状态改变其他元素的样式相关的知识,希望对你有一定的参考价值。

参考技术A focus,alink,hover,active都是伪类
:active
向被激活的元素添加样式。
:focus
向拥有键盘输入焦点的元素添加样式。
:hover
当鼠标悬浮在元素上方时,向元素添加样式。
:link
向未被访问的链接添加样式。

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

【中文标题】如何确保 CSS :hover 应用于动态添加的元素【英文标题】:How to ensure CSS :hover is applied to dynamically added element 【发布时间】:2012-10-18 17:03:55 【问题描述】:

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

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

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

如果仍然无法重现,您可以在调用anchor.show() 之前为fullimage.load 添加人为延迟。

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

Updated JS Bin with 1.5-second delay added (Hopefully makes issue clearer)

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

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

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

【问题讨论】:

只是一个旁注:您可以使用 .one() 代替使用 .on() 然后再次使用 .off() 解除绑定。见api.jquery.com/one @xec,酷,感谢您的提示! 设置一个jsfiddle,人们会更清楚地看到你的问题 @MildFuzz,最后有一个JS Bin链接。抱歉,我知道这可能会更明显。 仅供参考,.delay(1000) 不会延迟任何东西,因为它只会延迟动画。 .delay(1000).show(0) 可以解决问题 【参考方案1】:

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

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

【讨论】:

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

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

【讨论】:

他们当然应该是,我同意。但是,这不是我所看到的,因为我试图详细解释。你能重现我的问题吗? 谢谢你,我的强迫症又发作了:) 他们只是文字,伙计。只有文字。 ;) 当匹配元素添加到 DOM 时,伪类选择器变得适用是正确的;问题是图像加载时hover 状态没有更新 我投了反对票,因为这并不能以任何方式解决问题,而不是因为措辞。也许我刚刚重新阅读了关于此的 SO 政策时反应过度。随意编辑你的答案,我会撤消:)【参考方案3】:

[编辑:虽然我的解释可能很有趣,pozs' solution above 更好,所以如果可以的话,我建议使用它。]

hover 伪类规范是 quite relaxed,涉及何时应该激活它:

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

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

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

演示: JS Bin (based on your delayed example).

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;

【讨论】:

【参考方案4】:

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

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

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

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

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

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

这里是the spec的相关部分:

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

[...]

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

[...]

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

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

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

补充:我写完这个答案后,pozs提供的解决方案和上面基本一样!

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

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

是的,我想到了这一点,但是这个脚本旨在用于可能包含许多此类图像的页面,即使加载将异步完成,它也可能是一个不受欢迎的消耗带宽以加载可能未使用的几兆字节的图像。【参考方案6】:

你可以这样做: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/

希望对你有帮助

【讨论】:

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

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

);

【讨论】:

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

我这样做了,它在 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:对不起我的英语。

【讨论】:

【参考方案9】:

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

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

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

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

【讨论】:

很好的洞察力,但走这条路不允许它被动画化。但是,您是对的,如果不是动画,则在两个图像之间切换比缩放更大的图像要好。感谢您的回答! 实际上,通过使用 JavaScript 添加类,绕过了使用 :hover 的需要。请参阅 jsbin.com/ifitep/48 或 jsbin.com/ifitep/49 增加缩放效果(天空是极限!)。

以上是关于css 当中如何实现一个元素的hover, focus 状态改变其他元素的样式的主要内容,如果未能解决你的问题,请参考以下文章

html css hover 如何实现鼠标经过锚文本(不可点击字体)出现下划线?

css实现菜单栏效果以及用hover属性去控制另一个元素样式的问题

实现 css hover 以适用于所有 <a> 元素,其他元素之一

JS实现css的hover效果,兼容移动端

CSS On Hover - 将元素 ID1 更改为 ID2

React 中的内联 CSS 样式:如何实现 a:hover?