头图

如何在网页置灰的时候,部分元素保持彩色-有意思的面试题

在线体验

本文视频版链接

最近哀悼日,网页端如何一键变灰已经有很多实现方式了,但是我看到一个推文很有意思,是一个不错的面试题

现在网页置灰已经不仅仅是一行css的事了,如何在网页置灰的前提下,部分元素保持彩色,这是一个不错的system design题

一键变灰

这个大部分同学都写了,直接

html{
    filter: grayscale(100%);
}

考虑ie之类的兼容性的话,就直接把兼容性的属性都搞上去

html{
  -webkit-filter: grayscale(100%);
  -moz-filter: grayscale(100%);
  -ms-filter: grayscale(100%);
  -o-filter: grayscale(100%);
  filter: grayscale(100%);
  filter: gray;
  filter: progid:dximagetransform.microsoft.basicimage(grayscale=1);
}

如果想控制的更动态一些,可以用js控制html的class来实现这个切换过程

<button class="btn" id="set-gray">置灰</button>
let style = document.createElement('style')
let graySelector = 'gray-filter'
style.setAttribute('type', 'text/css')
// style.setAttribute('data-vite-dev-id', id)
style.textContent = `.${graySelector}{
  -webkit-filter: grayscale(100%);
  -moz-filter: grayscale(100%);
  -ms-filter: grayscale(100%);
  -o-filter: grayscale(100%);
  filter: grayscale(100%);
  filter: gray;
  filter: progid:dximagetransform.microsoft.basicimage(grayscale=1);
}`
document.head.appendChild(style)

let root = document.querySelector('html')
let btn = document.querySelector('#set-gray')
btn && btn.addEventListener('click', () => {
  setAllGray()
}, false)

function toggleClassName(el,name){
  if (el.className.indexOf(name) > -1) {
    el.className = el.className.replace(name, '').trim()
  } else {
    el.className = [el.className, name].join(' ')
  }
}

function setAllGray() {
  toggleClassName(root,graySelector)
}

这样可以在后端通过接口的形式决定是不是加载这段js就可以了

01.gif

那么问题来了,如何在置灰的前提下部分元素保持彩色呢

filter重置(失败)

如果能直接某个元素重置filter, 尝试下面的写法,但是不生效

html{
    filter:grayscale(100%);
}
.not-gray{
    filter:none;
}

如果filter的算法可逆的话,可以在.not-gray元素上设置一个翻转的filter,查了点资料,Chromium灰色100%的算法如下, 我本人图像处理方面比较菜,但是看起来全灰的算法不可逆,而且如果在元素上再盖一个canvas也不太好弄 放弃

R/G/B = 0.2126R' + 0.7152G' + 0.0722'B

遮挡解决方案 backdrop-filter

有一个解决方案是用backdrop-filter做一个遮罩,毕竟filter还是有点损耗首屏性能的,虽然可以用transform开启硬件优化一些,我们还可以用遮罩的方式挡住也可以的,并且设置pointer-events: none;不阻挡用户交互,也是一段css搞定

html {
    position: relative;
    width: 100%;
    height: 100%;
}
html::before {
    content: "";
    position: fixed;
    backdrop-filter: grayscale(100%);
    pointer-events: none;
    inset: 0;
    z-index: 100;
}

还可以把遮罩的position换成absolute, 实现一个只置灰首屏的效果,不过我感觉没啥必要

02.gif

然后我们可以设置指定元素的z-index,超过backdrop-filter的100就可以, 就有首屏+部分彩色的效果

05.gif

.not-gray{
  position: relative;
  z-index:1000;
}

元素遍历标记

backdrop-filter其实也有他的兼容性问题,尤其是firefox版本102(最新107)之前都不能用,filter方案更普及一些,不过作为面试题的话 我们还可以继续用filter这个方法,

image.png

image.png

我们设置有一些选择器保持彩色,然后统计出当前这个网页中,需要置灰的元素,网页是一个属性结果,我们先对选中元素的父元素进行遍历标记

06.jpeg


let body = document.body
//配置选择器,命中这个列表选择器的不置灰
let selectors = ['#not-gray2', '.not-gray3']
selectors.forEach(selector=>{
    let doms = [...document.querySelectorAll(selector)].forEach(v=>{
      if(!v) return 
      v.staycolor = true
      let parent = v.parentNode
      while(parent && !parent.colorful){
        parent.colorful = true
        parent = parent.parentNode
      }
    })
})

然后现在需要置灰的元素都已经标记了colorful,然后我们遍历一下,递归每个child,如果没有colorful,直接置灰返回,通过递归就可以把所有元素都置灰了

let graySelector = 'gray-filter'
walk(body)

function walk(node){
    if(node.nodeType!==1) return 
    if(node.staycolor) return 
    if(!node.colorful){
      toggleClassName(node,graySelector)
      return
    }
    for (var i = 0; i < node.children.length; i++) {  
      var child = node.children[i]; 
      walk(child)
    }  
}

可以把selectors做成从后端读取,就可以动态设置保持彩色的部分了, 不过这样设置filter可能会导致部分元素的定位失效,不过作为面试题的追问还不错

04.gif

总结

作为面试题来说,考察了面试者的css,js的dom遍历,递归思想,很不错的入门题


81 声望
13 粉丝
0 条评论
推荐阅读
手把手教你写一份优质的前端技术简历
不知不觉一年一度的秋招又来了,你收获了哪些大厂的面试邀约,又拿了多少offer呢?你身边是不是有挺多人技术比你差,但是却拿到了很多大厂的offer呢?其实,要想面试拿offer,首先要过得了简历那一关。如果一份简...

tonychen153阅读 18k评论 5

封面图
正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青57阅读 8.7k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy49阅读 7.4k评论 12

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs42阅读 7k评论 12

封面图
CSS 绘制一只思否猫
欢迎关注我的公众号:前端侦探练习 CSS 有一个比较有趣的方式,就是发挥想象,绘制各式各样的图案,比如来绘制一只思否猫?思否猫,SegmentFault 思否的吉祥物,是一只独一无二、特立独行、热爱自由的(&gt;^ω^&lt...

XboxYan48阅读 3.4k评论 14

封面图
「多图预警」完美实现一个@功能
一天产品大大向 boss 汇报完研发成果和产品业绩产出,若有所思的走出来,劲直向我走过来,嘴角微微上扬。产品大大:boss 对我们的研发成果挺满意的,balabala...(内心 OS:不听,讲重点)产品大大:咱们的客服 I...

wuwhs32阅读 3.6k评论 5

封面图
还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan35阅读 2.7k评论 2

封面图
81 声望
13 粉丝
宣传栏