在线体验本文视频版链接最近在悼念的日子,网页上已经有很多实现一键变灰的方法,但是看到一条很有意思的推文,不错面试题。现在网页变灰不只是一行css如何在网页变灰的前提下,让一些元素保持颜色?这是一个很好的系统设计问题。大多数同学都写过。直接html{filter:grayscale(100%);}如果考虑到ie之类的兼容性,可以直接把所有的兼容性属性加到html{-webkit-filter:grayscale(100%);-moz-filter:灰度(100%);-ms-filter:灰度(100%);-o-滤镜:灰度(100%);滤镜:灰度(100%);过滤器:灰色;filter:progid:dximagetransform.microsoft.basicimage(grayscale=1);}如果想控制的更动态,可以用js控制html的class来实现这个切换过程设置灰色letstyle=document.createElement('style')letgraySelector='gray'-filter'style.setAttribute('type','text/css')//style.setAttribute('data-vite-dev-id',id)style.textContent=`.${graySelector}{-webkit-filter:grayscale(100%);-moz-filter:灰度(100%);-ms-filter:灰度(100%);-o-滤镜:灰度(100%);滤镜:灰度(100%);过滤器:灰色;过滤器:progid:dximagetransform.microsoft。basicimage(grayscale=1);}`document.head.appendChild(style)letroot=document.querySelector('html')letbtn=document.querySelector('#set-gray')btn&&btn.addEventListener('点击',()=>{setAllGray()},false)functiontoggleClassName(el,name){if(el.className.indexOf(name)>-1){el.className=el.className.replace(name,'').trim()}else{el.className=[el.className,name].join('')}}functionsetAllGray(){toggleClassName(root,graySelector)}这个可以通过接口的形式来判断在后端加载这个js就够了吗?那么问题来了,如何在变灰的前提下,让一些元素保持颜色呢?Filterreset(failure)如果可以直接为某个元素重置filter,试试下面的写法,但是不行html{filter:grayscale(100%);}.not-gray{filter:none;}如果filter算法是可逆的,可以在.not-gray元素上设置一个reversefilter,查了些资料,Chromiumgray100%的算法如下,我擅长图像处理,不过好像全灰的算法是不可逆的,不容易用画布覆盖元素。放弃R/G/B=0.2126R'+0.7152G'+0.0722'B遮挡解决方案backdrop-filter一种解决方案是使用backdrop-filter做一个mask。毕竟滤镜还是失去了第一屏的性能。虽然可以使用transform来开启硬件优化,但是我们也可以使用mask来屏蔽它也是可以的,设置pointer-events:none;不阻塞用户交互,也是一块css来处理html{位置:相对;宽度:100%;高度:100%;}html::before{内容:“”;位置:固定;背景滤镜:灰度(100%);指针事件:无;插图:0;z-index:100;}也可以把mask的position改成absolute来实现只让首屏变灰的效果,不过我觉得没必要然后我们可以设置指定的z-indexelement超过backdrop-filter100就够了,就会有首屏+部分颜色的效果。非灰色{位置:相对;z-index:1000;}元素遍历标签backdrop-filter其实有它的兼容性问题,尤其是firefox102版本(最新的107)之前是不可用的,filterscheme比较流行,但是作为一个面试题,我们可以继续使用过滤方法。我们设置一些选择器保持颜色,然后统计当前网页中需要灰显的元素。网页是一个属性结果,我们先遍历选中元素mark的父元素letbody=document.body//配置选择器,命中列表选择器的非灰色letselectors=['#not-gray2','.not-gray3']selectors.forEach(selector=>{letdoms=[...document.querySelectorAll(selector)].forEach(v=>{if(!v)returnv.staycolor=trueletparent=v.parentNodewhile(parent&&!parent.colorful){parent.colorful=trueparent=parent.parentNode}})})现在需要变灰的元素已经被标记为彩色了,然后我们遍历并递归每个孩子。如果没有五颜六色的,就把它变灰然后退货。通过递归,可以将所有元素置灰。让graySelector='gray-filter'walk(body)functionwalk(node){if(node.nodeType!==1)returnif(node.staycolor)returnif(!node.colorful){toggleClassName(node,graySelector)返回}for(vari=0;i