CSS 小技巧!如何保留 hover 的状态? - 掘金

713 阅读10分钟

今天跟大家介绍一下css中transition的使用小技巧,我们先来看一个首页排行榜效果:

image.gif

怎么样,是不是一个很熟悉的效果,那么很多人可能第一时间就会想到用js来实现,但是,css能够实现的又何必动用js呢?在我看来,js就应该回归本职,专心处理数据逻辑交互,视觉方面交给css就行了。这里要介绍的就是transition属性延迟的一些小技巧,一起来看看吧!

这里的逻辑交互有以下几个,我们一次来进行实现:

  1. 鼠标滑过触发选中状态;
  2. 鼠标移出列表后仍然保留上一次的选中状态;(重点)
  3. 默认列表的第一项为选中状态;

一、鼠标滑过触发选中状态

我们先简单布一下局

<ul class="list">
      <li class="item">
        <h3 class="title">将军,夫人喊你种田了</h3>
        <p class="sumary">
          只是在休息室里打了个盹儿,一睁眼,竟然穿成了古代目不识丁的乡下胖丫头。
          好吃懒做不说,还在村里横行霸道。
          十里八乡没人愿意娶她,好不容易买了个金龟婿,大婚之日竟让人逃了。
          恶霸老爹一怒之下去道上掳了个夫君给她。
          就是……爹你掳的是不是有点不太对呀? * 婚后的苏胖丫很忙。
          忙着改造恶霸爹爹与恶霸弟弟。 忙着抢救貌美如花的神将夫君。
          忙着养育三个小小恶霸小豆丁。
          一不小心,将自己忙成了大燕最位高权重的一品女侯!
        </p>
      </li>
      <li class="item">
        <h3 class="title">被夺一切后她封神回归</h3>
        <p class="sumary">
          【甜爽燃,团宠,玄学】
          司扶倾一睁眼,不仅被夺了气运,人人还让她滚出娱乐圈。
          重活一次,她只想咸鱼躺,谁知现在圈内人只知拉踩营销,没点真本事,不好好磨炼演技,这样下去还能行?怎么也得收拾收拾。
          司扶倾捏了捏手腕,动了。
          后来,网上疯狂骂她不自量力倒贴郁曜,造谣她私生活不检点,而——
          国际天后:今天我能站在这里,多亏了倾倾 top1男顶流:离我妹妹远点@郁曜
          就连国际运动会官方:恭喜司扶倾拿下第13枚个人金牌,等一个退圈
          当天,全网瘫痪。 ·
          史书记载,胤皇年少成名,八方征战,平天下,安宇内,是大夏朝最年轻的帝王,他完美强大,心怀天下,却因病死于27岁,一生短暂,无妻无妾,无子无孙,是无数人的白月光男神。
          无人知晓,他再睁开眼,来到了1500年后。
          这一次,他看见了他遥想过的盛世大夏。 ·
          不久后胤皇身份曝光,司扶倾得知偶像竟然就在身边,她敬佩万分,只想——
          司扶倾:努力奋斗,报效大夏! 胤皇:以身相许 司扶倾:???
          我一心奋发上进你却想要我? · 全能颜巅女神×杀伐清贵帝王
          从全网黑到封神顶流,顺便和男神1v1
        </p>
      </li>
      <li class="item">
        <h3 class="title">簪星</h3>
        <p class="sumary">
          社畜杨簪星在连续五天熬夜加班后,穿进了自己上个月看的一本古早男频修仙爽文里,并成功当上了出场三千字就因陷害男主青梅竹马被男主一掌轰死的炮灰女配。
          杨簪星:有事吗???
          熟知剧情的社畜发出振聋发聩的呐喊:我命由我不由天哈! 然而.....
          四十年前的妖鲛归来复仇,秘境中遗留下的神女遗迹,凋零的蛇巫一族再现人间,更有传说中的神剑重见天日......
          老天爷是不是玩不起??
          面对逐渐升级的剧情难度,簪星只有一个目的——苟到大结局。
          ——天命,是无论如何都会发生的事,不管你怎么挣扎,结局都不会改变。
          ——我偏要改变。
          一个我命由我不由原著的故事,口是心非臭屁小师叔x努力苟到结局大师侄,女主有金手指,请支持正版茶~
        </p>
      </li>
      <li class="item">
        <h3 class="title">将军,夫人喊你种田了</h3>
        <p class="sumary">
          只是在休息室里打了个盹儿,一睁眼,竟然穿成了古代目不识丁的乡下胖丫头。
          好吃懒做不说,还在村里横行霸道。
          十里八乡没人愿意娶她,好不容易买了个金龟婿,大婚之日竟让人逃了。
          恶霸老爹一怒之下去道上掳了个夫君给她。
          就是……爹你掳的是不是有点不太对呀? * 婚后的苏胖丫很忙。
          忙着改造恶霸爹爹与恶霸弟弟。 忙着抢救貌美如花的神将夫君。
          忙着养育三个小小恶霸小豆丁。
          一不小心,将自己忙成了大燕最位高权重的一品女侯!
        </p>
      </li>
      <li class="item">
        <h3 class="title">被夺一切后她封神回归</h3>
        <p class="sumary">
          【甜爽燃,团宠,玄学】
          司扶倾一睁眼,不仅被夺了气运,人人还让她滚出娱乐圈。
          重活一次,她只想咸鱼躺,谁知现在圈内人只知拉踩营销,没点真本事,不好好磨炼演技,这样下去还能行?怎么也得收拾收拾。
          司扶倾捏了捏手腕,动了。
          后来,网上疯狂骂她不自量力倒贴郁曜,造谣她私生活不检点,而——
          国际天后:今天我能站在这里,多亏了倾倾 top1男顶流:离我妹妹远点@郁曜
          就连国际运动会官方:恭喜司扶倾拿下第13枚个人金牌,等一个退圈
          当天,全网瘫痪。 ·
          史书记载,胤皇年少成名,八方征战,平天下,安宇内,是大夏朝最年轻的帝王,他完美强大,心怀天下,却因病死于27岁,一生短暂,无妻无妾,无子无孙,是无数人的白月光男神。
          无人知晓,他再睁开眼,来到了1500年后。
          这一次,他看见了他遥想过的盛世大夏。 ·
          不久后胤皇身份曝光,司扶倾得知偶像竟然就在身边,她敬佩万分,只想——
          司扶倾:努力奋斗,报效大夏! 胤皇:以身相许 司扶倾:???
          我一心奋发上进你却想要我? · 全能颜巅女神×杀伐清贵帝王
          从全网黑到封神顶流,顺便和男神1v1
        </p>
      </li>
      <li class="item">
        <h3 class="title">簪星</h3>
        <p class="sumary">
          社畜杨簪星在连续五天熬夜加班后,穿进了自己上个月看的一本古早男频修仙爽文里,并成功当上了出场三千字就因陷害男主青梅竹马被男主一掌轰死的炮灰女配。
          杨簪星:有事吗???
          熟知剧情的社畜发出振聋发聩的呐喊:我命由我不由天哈! 然而.....
          四十年前的妖鲛归来复仇,秘境中遗留下的神女遗迹,凋零的蛇巫一族再现人间,更有传说中的神剑重见天日......
          老天爷是不是玩不起??
          面对逐渐升级的剧情难度,簪星只有一个目的——苟到大结局。
          ——天命,是无论如何都会发生的事,不管你怎么挣扎,结局都不会改变。
          ——我偏要改变。
          一个我命由我不由原著的故事,口是心非臭屁小师叔x努力苟到结局大师侄,女主有金手指,请支持正版茶~
        </p>
      </li>
    </ul>
复制代码

然后再简单修饰一下

.list {
      list-style: none;
      margin: 0;
      padding: 0;
      width: 400px;
    }
    .item {
      position: relative;
      padding: 10px 10px 10px 34px;
      cursor: pointer;
      counter-increment: num;
    }
    .title::before {
      content: counter(num) " ";
      width: 25px;
      line-height: 30px;
      text-align: center;
      color: #fff;
      position: absolute;
      font-size: 14px;
      font-family: fantasy;
      left: 4px;
      background: center/100% 100%
        url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAA8CAMAAAAe9Wm0AAAAbFBMVEUAAACaov2SnPKXnvaXofiyuvuCjPOgqvi4vfiBjPSyuf6Ml/a0u/21vPtte/F/i/WTnfWNmPiyuv6Ik/Zue/GRnPinr/Wfp/Gyuv6Ml/ebo/KTnfOutfqEj/W3vvpue/G/xfd1gvN8iPRodfBFzp+BAAAAF3RSTlMAECA7U2BgdIiTn6Wsvr+/v9Df39/s/o6+GugAAAGPSURBVHjazdZhk4IgEAZgpS6z8iotPEBF8P//x1tJrTxQlrm5uff7MxubLBtF9sQfp+ywjRCJk+zLhPq6SUwuxokhp4/YT1SUVtWam4nSZObImyA2YXHZ022cYsirOzwYoYti7nJzuMOqeHPNvke5jxgP2DRNNqLSMzQQtXjUtv8cBTWiwqOqohcsKikt8ajkHI+4EGjEBWNnNGJ1jUe1Vp9YJLSSeKRkh0eyC0BdEJIBZ5J/h1TA/6SUOgcgjUdaT6gJQA0CjV85jFrq/ZU/UYtA7HFzG5iA1PcOMnbs0b7tU/mcCAhjO/NE52A8jmXKMHYdnukTzE141vhyGWaSTq97ksMQhOnEF8qYFJvXneDO++lU82VznG0giYDppBVzTTvIffdjZSGFVjA2JHeVuRDbopOC6TrJ7GUSx0a1vYGR8u1kQ5krce5hJAUjlRbuRtuyu8GFVopxZ6OtxW5wObU2P3Ewaby+jya6R7UYO+C3xpICbk3NFhptTToaZ6OtzS/WGm39iUdkmdFFv5pvdNPREWjicTcAAAAASUVORK5CYII=);
    }
    .title {
      margin: 0;
      padding: 8px 0;
      font-weight: normal;
    }
    .sumary {
      margin: 0;
      overflow: hidden;
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      color: #666;
      font-size: 14px;
      color: 20px;
      height: 0;
    }

    .item::before {
      content: "";
      position: absolute;
      inset: 0;
      border-radius: 8px;
      opacity: 0;
      z-index: -1;
      background: linear-gradient(
        270deg,
        rgb(241, 236, 249) 0%,
        rgba(241, 236, 249, 0) 100%
      );
    }
复制代码

最后的效果是这样的

image2.png

现在再加上hover效果

    .item:hover .title {
      color: rebeccapurple;
    }
    .item:hover .sumary {
      height: 40px;
    }

复制代码

效果如下:

image3.gif 那么如何在鼠标移出后仍然保持最后的状态呢?接着往下看

二、保留hover的状态

实现保留hover状态需要用到这样一个小技巧 比如,给一个元素添加hover样式

.el:hover{
    color:red;
}
复制代码

image4.jpg 如果我们给这个元素加一个延迟

.el{
   transition-delay:1s; 
}
复制代码

那么鼠标在移入移出时都会有延迟

image5.jpg 接着,我们在hover的时候取消延迟

.el:hover{
    color:red;
    transition-delay:0s;
]
复制代码

那么鼠标移入的时候会迅速响应,移出的时候仍然会有延迟

image6.jpg 那么如果把延迟设置到足够大时,比如

.el{
    transition-delay:9999s
}
复制代码

这样鼠标在移出后,需要经历9999s后才会变为原来的样子,也就相当于保留了hover状态

image7.jpg

三、鼠标移除列表后仍然保留上一次的选中状态

根据上面的原理,我们可以这样操作:

    /*默认transition*/
    .item::before,
    .item .sumary,
    .item .title {
      transition: 0s 9999s;
    }
    /*每一项hover*/
    .item:hover .title {
      color: rebeccapurple;
      transition: none;
    }
    .item:hover .sumary {
      height: 40px;
      transition: none;
    }
复制代码

27c008c869605eee45d3ac4507b23810.jpg 需要注意的是,由于是transition,所以所有的状态变化都是需要支持过渡属性的,比如隐藏sumary这里用的是height:0而不是display:none。效果如下:

image8.gif

这样在鼠标离开后,之前的状态仍然是保留的,但是我们只需要保留上一次的,而不是所有的,该如何处理呢?

这里需要换一种思路,可以这么做,鼠标在移入整个列表的时候就清除所有的状态,这样就只有当前hover的选项才会保留下来,有点类似于js中的思维了,先把所有选中状态的类名移除,再给当前项添加选中的类名,实现如下:

    .list:hover .title {
      transition: none;
      color: #333;
    }
    .list:hover .sumary {
      transition: none;
      height: 0;
    }
复制代码

这样就实现了鼠标移出列表后仍然保留上一次的选中状态的功能了,有点像单选框的效果,只不过是hover触发的,效果如下:

image9.gif

四、默认列表的第一项为选中状态

下面来实现最后一个功能。 这个相对而言比较容易,需要用到first-child伪类,可以匹配到第一个元素。 不过需要考虑的是优先级的问题,这个是默认状态,权限应该是最低的,其他hover样式都应该可以覆盖它,所以应该放在最前面,如下

    .item:first-child .sumary{
      height: 40px;
    }
    .item:first-child .title{
      color:rebeccapurple;
    }
复制代码

这样就完美实现了文章开头的效果! 由于是css实现,多个列表也是可以完全复用的

image10.gif

放在最后

下面来总结一下吧

  1. 实现的原理就是利用transition:delay,让还原的时间足够长,这样就实现了保留hove状态的效果了;
  2. 单选效果可以在鼠标移入整个列表的时候就清除所有的状态,这样就只有当前hover的选项才会被保留下来,有点类似于js中的思维;
  3. 需要注意的是所有属性必须是支持transition的,比如display:none就不支持transition,需要用其他样式替代;