200 Success
200 Success 博客
200 Success
常用标签
组员
您正在查看: css 分类下的文章

相比<p>、<div>这样的标签,
input标签表现得更加复杂,而它是怎么实现的呢?

在这里我们要引出一个概念

Shadow DOM

Shadow DOM是HTML的一个规范 ,它允许浏览器开发者封装自己的HTML标签、CSS样式和特定的javascript代码,同时也可以让开发人员创建类似<video>这样的自定义一级标签,创建这些新标签内容和相关的的API被称为Web ComponentWeb Component内容比较多不在这里讲,有空再写一篇



在平时我们是没办法从DevTools中看到Shadow DOM的,因为默认是隐藏的,需要手动设置,

DevToolsDevTools



来一个例子,我们来看看一个input组件

在没有显示Shadow DOM之前 它在HTML中是长这个样子的

展开之后 是这个样子

可以看出在input标签内出现了#shadow-root(user-agent),它包含了input组件内的DOM结构,
这就是所谓的Shadow DOM

Shadow DOMShadow DOM

结合代码和这两张图片我们可以来理一理Shadow DOM的结构
从外到内分别是shadow-hostshadow-rootshadow-tree

我们可以来看看W3C对Shadow DOM的描述

Shadow host: The regular DOM node that the shadow DOM is attached to.
Shadow host是一个被Shadow DOM附着的普通DOM节点
Shadow tree: The DOM tree inside the shadow DOM.
Shadow tree是属于Shadow DOM下的节点树
Shadow root: The root node of the shadow tree.
Shadow root是Shadow tree的根节点
Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
Shadow boundary是Shadow DOM结束,普通DOM开始的分界线

同时我们可以看见shadow-tree的元素中出现了我们不熟悉的属性pseudo

何为pseudo
我们可能用过这样的方式来隐藏掉浏览器默认的丑陋滚动条

body::-webkit-scrollbar{display:none;}

对的,这个pseudo就是伪元素Pseudo Element

有了它,
我们可以在css中从shadow-host通过pseudo来访问shadow-root下的元素


Shadow DOM的好处

一个东西存在必然有它的价值,Shadow DOM为我们提供了简单有效的样式隔离,除此之外和普通DOM元素没有差别
在某个Shadow DOM内写的css代码并不会对该Shadow DOM以外的元素生效,于是我们可以通过Shadow DOM来隐藏控件的内部实现,就不用担心对样式表进行修改时对控件造成影响了

下面我们来看看Shadow DOM的简单使用

笔者找到两种方式来创建Shadow root

  • 源自MDNElement.attachShadow(shadowRootInit)
    最后修改时间: 2020.01.08
  • 源自简书帖子的Element.createShadowRoot()
    最后修改时间: 2018.10.11

都可以通过来创建Element(Shadow host)下的Shadow root

笔者测试Element.attachShadow(shadowRootInit)在现代浏览器的兼容性更强
并且Element.createShadowRoot()不是W3C标准,MDN并不建议使用
于是以下例子均用Element.attachShadow(shadowRootInit)
Element.createShadowRoot()有兴趣的可以跳转简书帖子阅读

详细说一说MDNElement.attachShadow(shadowRootInit)

可以作为Shadow host使用attachShadow()的标签有

一个有合法名字的自定义标签、<article>、<aside>、<blockquote>、<body>、<div>、<footer>、<h1>、<h2>、<h3>、<h4>、<h5>、<h6>、<header>、<main>、<nav>、<p>、<section>、<span>

其中的shadowRootInit包括以下字段

mode

  1. open

    • 可以从根外部的JavaScript访问Shadow root的元素
    • Element.shadowRoot; // Returns a ShadowRoot obj
  2. closed

    • 不可以从根外部的JavaScript访问Shadow root的元素
    • Element.shadowRoot; // Returns null

delegatesFocus

  1. true

    • 点击Shadow Root的某个不可focus元素时,会聚焦到另外第一个可以focus的元素上,并为Shadow host提供任何可用的:focus样式
  2. false

    • 和true反过来

这是MDN提供的兼容图

下面我们用一些例子来帮助我们理解

比方说现在的HTML代码是这样的

<body>
    <h1>outer h1</h1>
    <div id='test'>
    </div>
</body>

然后我们对#test添加Shadow DOM并且在Shadow DOM里添加css样式

if (document.body.attachShadow) { //做兼容性处理
    let test = document.querySelector('#test');
    let shadow = test.attachShadow({
        mode: 'open'
    });
    shadow.innerHTML = `
        <h1>inner h1</h1>
        <style>
            h1{color:red;text-align:center}
        </style>
    `;
}

效果图

代码图

可以看到Shadow DOM里的样式并没有泄露出来,这给我们创建Web Component奠定了基础

这是一个简单的关于transition与overflow属性的小demo

在线示例
body {
    margin: 0;
    padding: 0;
}

html {
    font-size: 62.5%;
}

#test-box,
#test-box-1 {
    width: 20%;
    height: 20rem;
    background-color: skyblue;
    transition: height 0.5s;
    margin-left: 1rem;
    margin-bottom: 1rem;
    /* div块高度为0时,内部元素都因溢出而隐藏,但有滚动条,可隐藏。 */
    overflow: auto;
}

#test-box h1 {
    /* 给定宽度*/
    width: 100%;
    /* 换行处理:不换行 */
    white-space: nowrap;
    /* 溢出内容做隐藏处理 */
    overflow: hidden;
    /* 溢出的隐藏内容用省略号替代 */
    text-overflow: ellipsis;
}

#test-box::-webkit-scrollbar {
    display: none;
}

.box-close {
    height: 0 !important;
    /* display: none;  不可配合transiton使用 */
}

#btn-box {
    display: flex;
    flex-direction: row;
    align-items: center;
    margin-left: 1rem;
}

#btn {
    border: 1px solid red;
    width: 1rem;
    height: 1.5rem;
    margin: 0.5rem;
    border-radius: 0.5rem;
    margin-right: 1rem;
    transition: 0.5s;
}

.btn-change {
    transform: rotate(360deg);
}
<body>
    <div id="btn-box">
        <!-- 触发一个事件若要调用多个函数,可用逗号相隔 -->
        <div id="btn" onclick="div_change(),btn_change(this)"></div>
        <span class="btn-span">点我</span>
    </div>
    <div id="test-box">
        <h1>1This is a transition test!This is a transition test!</h1>
        <h1>2This is a transition test!</h1>
        <h1>3This is a transition test!</h1>
        <h1>4This is a transition test!</h1>
        <h1>5This is a transition test!</h1>
        <h1>6This is a transition test!</h1>
    </div>
    <div id="test-box-1">
        <h1>1This is a transition test!</h1>
    </div>
</body>
<script>
    function div_change() {
        let testBox = document.getElementById('test-box');
        if (!testBox.className) {
            testBox.className = 'box-close';
        } else {
            testBox.className = '';
        }
    }

    function btn_change(e) {
        if (!e.className) {
            e.className = 'btn-change';
        } else {
            e.className = '';
        }
    }
<script>