整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

一次性清除“知乎”所有DIV

一次性清除“知乎”所有DIV

者:卡颂

转发链接:https://mp.weixin.qq.com/s/ICjOlJL-fUGRb2S_xqBT7Q

是个无聊的问题吗当提出这个问题时,我的第一反应是——我还真是无聊 ?♂?


转念一想,em~,好像事情没有这么简单如果直接选择所有div再遍历删除的话,div的子孙节点也被删除了。有什么办法能在保留整棵DOM树层级关系的前提下,只删除div节点呢?我陷入了沉思。。。。。

知乎是React写的,React用JSX来表示页面层次结构,JSX在编译时会被babel转换为React.createElement。在代码运行时,React获取的其实是React.createElement()的返回值。办法来了!

我们只需要覆写一下React.createElement方法,当遇到type==='div',我们将type修改为React.Fragment,即我们把所有div节点都变成Fragment,那不就能在保持树的层级关系的同时去掉div了?让我们开始吧!!如何拿到React对象这时候遇到了第一个问题:知乎没把React暴露到全局(废话,当然不会 ),怎么获取React对象呢?

好在当我们使用React Dev Tools时,Dev Tools会向页面注入全局变量__REACT_DEVTOOLS_GLOBAL_HOOK__,这个变量会成为链接React与Dev Tools的桥梁。

其中的renderers属性指页面使用的渲染器,在网页端就是我们熟悉的React-DOM(在客户端当然就是React-Native啦)我们发现一个方法findFiberByHostInstance

方法名居然出现了Fiber字样!!

我们知道,Fiber是React的最小可调度单元(别问为什么,问就是安利我写的React源码揭秘系列)那findFiberByHostInstance方法所在文件一定有React相关定义。我们右键跳转到定义函数的文件,

在文件内搜.createElement

果然让我们找到了。打上断点,刷新页面试试~

果然进来了,事情越发有趣了看看o.a是什么,em~~一个对象,内部有Children、createElement。。。。。。看来这就是React对象了

赶忙把来之不易的React对象保存在全局,顺便把React.createElement也保存一份。

现在放开断点,window.React已经指向知乎首页内部使用的React啦。

修改React.createElement方法我们知道,React.createElement方法第一个参数为type(别问为什么,问又是一波安利React源码揭秘系列),我们再到React文档中找来React.Fragment的定义。

if (typeof Symbol==='function' && Symbol.for) {
  const symbolFor=Symbol.for;
  REACT_ELEMENT_TYPE=symbolFor('react.element');
  REACT_PORTAL_TYPE=symbolFor('react.portal');
  REACT_FRAGMENT_TYPE=symbolFor('react.fragment');
  REACT_STRICT_MODE_TYPE=symbolFor('react.strict_mode');
  REACT_PROFILER_TYPE=symbolFor('react.profiler');
  REACT_PROVIDER_TYPE=symbolFor('react.provider');
  // ...
}

接下来,修改全局变量

React.createElement=(type, ...args)=> {
    if (type==='div') {
        type=Symbol.for('react.fragment');
    }
    // originCreateElement才是正经的React.createElement
    return originCreateElement(type, ...args);
}

让我们康康此时的页面结构

ok~~~ 满屏的div套div(嫌弃脸),接着我们轻轻的点一下关注按钮,触发随便啥组件的setState。接下来,就是见证奇迹的时刻。。。

除了根节点,其他div都消失了,终于恢复了往日的清爽界面(大误)

总结通过这篇无聊的文章,我们认识到:

  1. React每次触发setState更新,都会重新遍历整棵Fiber树。(否则也不会点击一个按钮整个页面的div都消失)
  2. React.createElement的深度应用。
  3. 知乎真是有太多div了

PS:如果你用Chrome将被压缩后的代码formatted后打上断点,刷新页面进入断点后浏览器卡死,不要怀疑,这是Chrome的bug,截止版本 81.0.4044.138(正式版本) (64 位)还未修复,建议使用chrome开发者版本 Chrome Canary


作者姓名:卡颂


转发链接:https://juejin.im/post/5ecb2af0

首先我们通过一张图来解释下元素浮动是什么样的状态。

元素浮动情况

上图的html部分代码为:

html代码

css部分代码为:

css代码

通过上面的图片可以看出,当内部的div设置float后,外部的元素的就会受到以下影响:

  • 背景不能显示

  • 边框不能被撑开

  • margin和padding设置的值不能正确显示

元素浮动的坏处

如果不清除元素浮动的话,浮动层后面的非浮动层内容就有可能被覆盖,造成页面的排版混乱。

例如在上面的例子中再加一个非浮动的div,就会发现该div被前面的三个浮动div覆盖住。

因为浮动,后面的div被覆盖

既然会出现这种情况,我们就应该设法清除掉元素的浮动。

方法1-父级元素设定定高

我们可以给父级元素.outer设置为定高,这样就可以不影响后面的非浮动元素。.outer的样式为:

设置定高

达到的效果为

清楚浮动之后

虽然这种方法可以清楚浮动,但是对父元素设置定高会有很多的限制,如果内层div的高度变化,则需要手动修改外层div的高度,相对来说有点麻烦,因此这种方法并不推荐。

方法2-添加新元素,设置clear:both

在父元素内部的末尾处添加一个新的div,并设置其clear: both;属性。

添加一个新的div

该新增div的css属性为:

新增div的css属性

利用该方法同样可以达到清楚浮动的效果,但是由于会新增DOM元素,这种方法也不推荐。

方法3-父级元素使用overflow:hidden属性

在父级元素上使用overflow:hidden属性,准确来说只要不给overflow属性设置为visible都可以清除元素浮动。

设置overflow: hidden

使用这种方法可以少写很多css样式代码,同时不会新增DOM元素,因此推荐使用这种方式。

方法4-对父元素使用伪元素

通过对父元素使用:after伪元素,同样可以达到清除浮动的效果。

伪元素清楚浮动

以上方法在伪元素中,通过设置clear与overflow属性来实现。方法4在实际的项目中使用的频率最高,因此极大的推荐使用方法4来清除元素浮动。

其他方法

看到网上有的资料介绍,通过设置父元素为浮动,或者设置父元素为absolute定位,这两种方法只是能显示父元素而已,但父元素同样会覆盖到后面的非浮动元素,因此这两种方法严格意义来说并不算清除浮动的方法。

总结

今天这篇文章主要介绍了几种通过CSS来清除元素浮动的方法,你掌握了吗?