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 条评论
登录 后参与评论

相关文章

来自专栏抠抠空间

html基础

HTML简介 超文本标记语言(Hypertext Markup Language, HTML)是一种用于创建网页的标记语言。 本质上是浏览器可识别的规则,我们按...

35511
来自专栏柠檬先生

Angularjs基础(八)

AngularJS Bootstrap     AngularJS 的首选样式表是 Twitter Bootstrap ,Twitter Bootstrap ...

1906
来自专栏老马寒门IT

06Vue.js快速入门-Vue组件化开发

组件其实就是一个拥有样式、动画、js逻辑、HTML结构的综合块。前端组件化确实让大的前端团队更高效的开发前端项目。而作为前端比较流行的框架之一,Vue的组件和也...

2325
来自专栏一直在跳坑然后爬坑

里程表式跑马灯自定义控件

https://github.com/nelson1110/MarqueeView

822
来自专栏蔡述雄的专栏

包学会之浅入浅出Vue.js:结业篇

本篇我们会详细分析下如何完成由多个组件组成一个复用组件的开发流程。

15.6K24
来自专栏Web 开发

使用jQuery.data()查看元素上绑定的事件

最近遇到一个诡异的问题,发现我添加在一个HTMLElement片段上面的事件绑定,会在后续的流程中,无故丢失了。但是,我不知道它是什么时候丢失的。

730
来自专栏程序员的知识天地

深入解剖前端,你不知道的Web 组件标准

组件化使得复杂的前端结构变得清晰,各个部分独立起来,高内聚低耦合,使得维护成本大大降低。

373
来自专栏java一日一条

简单好看的Android圆形进度条对话框开源库

本文介绍CircleProgressDialog开源库的使用及实现的详细过程,该开源库主要实现以下几个功能:

812
来自专栏向治洪

高仿今日头条

高仿今日头条 --- 第一篇:(android高仿系列)今日头条 --新闻阅读器 (一) 上次,已经完成了头部新闻分类栏目的拖动效果。 这篇文章是继续去完善...

2229
来自专栏前端说吧

vue - 父组件数据变化控制子组件类名切换

先说当时的思路和实现 核心是父子组件传值和v-bind指令动态绑定class实现

591

扫码关注云+社区