shadow dom解析

1.shadowdom解析

1.1 什么是shadow dom

先看个例子:

<video controls autoplay name="media"> 
    <source id="mp4" src="http://media.w3.org/2010/05/sintel/trailer.mp4" type="video/mp4">
</video>

这样一个标签可以在浏览器产生几个界面你相对较复杂的播放器,怎么做到的? ??为了理解问题,可以选择chrome设置里面的show userAgent shawdow,就可以看到shadow dom里的内容。

<video src="a.mp4" width="480" height="360">
    #shadow root
    <div pseudo="-webkit-media-controls">
        <div pseudo="-webkit-media-controls-overlay-enclosure">
            <input type="button" style="display: none;">
        </div>
        <div pseudo="-webkit-media-controls-enclosure">
            <div pseudo="-webkit-media-controls-panel" style="display: none;">
                <input type="button" pseudo="-webkit-media-controls-play-button">
                <input type="range" step="any" pseudo="-webkit-media-controls-timeline" max="0">
                <div pseudo="-webkit-media-controls-current-time-display" style="display: none;">0:00</div>
                <div pseudo="-webkit-media-controls-time-remaining-display">0:00</div>
                <input type="button" pseudo="-webkit-media-controls-mute-button">
                <input type="range" step="any" max="1" pseudo="-webkit-media-controls-volume-slider" style="display: none;">
                <input type="button" pseudo="-webkit-media-controls-toggle-closed-captions-button" style="display: none;">
                <input type="button" style="display: none;">
                <input type="button" pseudo="-webkit-media-controls-fullscreen-button" style="display: none;">
            </div>
        </div>
    </div>
</video>

shadow-root里面的内容就是所有视频播放器控制组件的所在之处,css也可以看到,可见video标签内部也是很多个div和input形成的。 ??另外浏览器之所以将其置灰,是为了表明这部分是在 shadow DOM 里,对于页面的其他部分来说它是 不可用的 。这里的 不可用 意味着你写的 CSS 选择器和 JavaScript代码都不会影响到这部分内容。实际上,就是让video 标签的逻辑和样式都被浏览器封装了。

1.2 小结

小结下,Shadow DOM 是一个 HTML 的规范,其允许开发者封装自己的 HTML 标签、CSS 样式和 JavaScript代码。也使得开发人员可以创建诸如 video这样自定义的一级标签。总的来说,这些新标签和相关的 API 被称为 Web Components。

关于shadow 都没有些概念可以理解下,上面shadow root是shadow dom的根节点;shadow tree为这个show dom包含的节点树,div和input等;shadow host称为shadow dom的容器元素,即video

2.如何创建shadow dom

指定一个元素可以使用createShadowRoot方法创建一个shadow root,shadow root上可以任意通过dom操作添加shadow tree,同时制定样式和处理的逻辑,并将自己的api暴露出去。 完成创建后需要通过registerElement来注册元素。 (不过需要注意的是,目前支持chrome31、android4.4以上版本) 下面看个示例,是实现一个图文组合的组件功能:

<!doctype html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>图文组合插件</title>
    <!-- 导入组件 -->
    <link href="./image.html" rel="import" />
</head>
<body>
<x-image src="http://9.url.cn/edu/banner/img/aa73d17e_760_300.jpg" width="320" height="150" alt="banner自定义文本"></x-image>
</body>
</html>

组价模板的代码文件如下

<!-- 定义组件 -->
<template>
    <!-- 组件模板 -->
    <style>
        /* shadow host内容 */
        :host {
            display: block;
            font: 16px monospace;
        }
        .banner-section{
            margin: 0;
            padding: 0;
            width: 100%;
            display: block;
            position: relative;
        }
        .banner-section .banner-image{
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }
        .banner-section .banner-text{
            display: block;
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            padding: 10px 5px;
            color: #fff;
            background: rgba(0,0,0,0.5);
        }
        .banner-section .banner-text:hover{
            cursor: pointer;
        }
    </style>

    <div class="banner-section">
        <span class="banner-image">
            <img class='image' src="" alt="image" height="200">
        </span>
        <span class="banner-text">MM</span>
    </div>
</template>
<script>
    // 在本文件被导入后自动执行
    (function(thisDoc) {
        "use strict"; // 启用严格模式

        // 所有实例元素对象的公共prototype
        var XImage = Object.create(HTMLElement.prototype, {
            height: {
                get: function() { return this._height; },
                set: function(height) {
                    this._height = height;
                    this._innerBanner.style.height = height + 'px';
                    this._innerBanner.querySelector('.image').style.height = height +'px';

                }
            },
            width: {
                get: function() { return this._width; },
                set: function(width) {
                    this._width = width;
                    this._innerBanner.style.width = width + 'px';
                    this._innerBanner.querySelector('.image').style.width = width +'px';
                }
            },
            alt: {
                get: function() { return this._width; },
                set: function(alt) {
                    this._alt = alt;

                    this._innerBanner.querySelector('.banner-text') = alt;
                    this._innerBanner.querySelector('.image').setAttribute('alt', alt);
                }
            },
            src: {
                get: function() { return this._src; },
                set: function(src) {
                    this._src = src;
                    this._innerBanner.querySelector('.image').setAttribute('src', src);
                }
            }
        });
        // 组件被创建时执行,相当于构造函数
        XImage.createdCallback = function() {
            // 创建Shadow root,自定义模板放入其中
            var sr = this.createShadowRoot();
            var template = thisDoc.querySelector("template");
            var node = document.importNode(template.content, true);

            this._innerBanner = node.querySelector(".banner-section");

            var height = this._height || Number(this.getAttribute("height")),
                width = this._width || Number(this.getAttribute("width")),
                alt = this._alt || String(this.getAttribute('alt')),
                src = this._src || String(this.getAttribute('src'));

            if (!isNaN(height) || !isNaN(width)) {
                this.height = height;
                this.width = width;
            }
            if(alt){
                this.alt = alt;
            }
            this.src = src;
            sr.appendChild(node);
        };
        // 注册组件
        document.registerElement("x-image", { prototype: XImage });

    })(document.currentScript.ownerDocument); // ownerDocument指向被导入的文档对象(本文件)
</script>

html中使用html import方式引入外部的shadow dom内容,在支持shadow dom的浏览器上显示如下效果,同时在自定义的组件里可以按照自己的需要向外暴露可配置属性和webApi接口。

参考:

http://soledadpenades.com/2014/01/02/shadow-dom-in-firefox/

http://www.w3.org/TR/shadow-dom/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏vue学习

17、将数据渲染到组件(列表渲染、模板语法、父子组件之间的传值)

(1)v-for语法 https://cn.vuejs.org/v2/guide/list.html 我们用 v-for 指令根据一组数组的选项列表进行渲染...

571
来自专栏软件开发

前端MVC Vue2学习总结(五)——表单输入绑定、组件

你可以用 v-model 指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法...

562
来自专栏软件开发

前端MVC Vue2学习总结(五)——表单输入绑定、组件

一、表单输入绑定 1.1、基础用法 你可以用 v-model 指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,...

39814
来自专栏图像识别与深度学习

《HTML5实战》Lesson08

Week09    2016/11/09上午1-4节 一、完成文件编辑器浏览视图,代码3-1到3-3,效果如下图: ? 知识点如下: 1、display:bl...

2864
来自专栏柠檬先生

VUE 入门基础(8)

十,组件  使用组件   注册     可以通过以下这种方式创建一个Vue实例       new Vue({          el: ...

1879
来自专栏从零开始学 Web 前端

从零开始学 Web 之 DOM(二)对样式的操作,获取元素的方式

924
来自专栏python3

tkinter -- Text (3)

注意:first 没有使用 tag('b')属性,last 使用了 tag('b')属性

602
来自专栏韩东吉的Unity杂货铺

零基础入门 41:Hierarchy也可以这样玩

上一篇给大家说了下Inspector如何禁用RectTransform属性修改的做法,今天就来分享下对Hierarchy的另一种操作。

745
来自专栏HTML5学堂

一步步教你弹性框架-下篇

HTML5学堂:本文继续为大家讲解弹性框架,在前两篇文章当中,我们从最基本的来回运动,讲解到缓冲运动、有摩擦力的运动。基本实现了弹性动画效果。今天我们主要来进行...

3664
来自专栏黄Java的地盘

Markdown语法简介

markdown语法目前有两种,我会选择一种我在实践过程中认为比较简单的标记来进行简单介绍。如果需要进行查阅,可以直接阅读末尾总结部分。

341

扫码关注云+社区