必威-必威-欢迎您

必威,必威官网企业自成立以来,以策略先行,经营致胜,管理为本的商,业推广理念,一步一个脚印发展成为同类企业中经营范围最广,在行业内颇具影响力的企业。

原文出处必威:,不过在DOM标准中并没有定义o

2019-12-08 20:38 来源:未知

四、参考链接

  • John Negoita, Extending HTML by Creating Custom Tags
  • StackOverflow, Are custom elements valid HTML5?
  • Eric Bidelman, Custom Elements v1: Reusable Web Components

 

1 赞 1 收藏 评论

必威 1

概述

Shadow DOM 好像提出好久了,最本质的需求是需要一个隔离组件代码作用域的东西,例如我组件代码的 CSS 不能影响其他组件之类的,而 iframe 又太重并且可能有各种奇怪问题。

可以这么说,Shadow DOM 旨在提供一种更好地组织页面元素的方式,来为日趋复杂的页面应用提供强大支持,避免代码间的相互影响。

看下在 chrome 它会是咋样的:

必威 2

我们可以通过 createShadowRoot() 来给一个元素节点创建 Shadow Root,这些元素类型必须是下边列表的其中一个,否则会抛出 NotSupportedError 异常。

  • 自定义的元素
  • article
  • aside
  • blockquote
  • body
  • div
  • header, footer
  • h1, h2, h3, h4, h5, h6
  • nav
  • p
  • section
  • span

createShadowRoot() 是现在 chrome 实现的 API,来自文档:https://www.w3.org/TR/2014/WD…。最新的文档 API 已经调整为 attachShadow()

返回的 Shadow Root 对象从 DocumentFragment 继承而来,所以可以使用相关的一些方法,例如shadowRoot.getElementById('id') 来获取 Shadow DOM 里边的元素。

简单的使用如下:

JavaScript

const div = document.getElementById('id') const shadowRoot = div.createShadowRoot() const span = document.createElement('span') span.textContent = 'hello world' shadowRoot.appendChild(span)

1
2
3
4
5
6
const div = document.getElementById('id')
const shadowRoot = div.createShadowRoot()
const span = document.createElement('span')
 
span.textContent = 'hello world'
shadowRoot.appendChild(span)

在这里,我把这个 div 成为是这个 Shadow DOM 的 宿主元素,下边的内容会延续使用这个称呼。

Shadow DOM 本身就为了代码隔离而生,所以在 document 上使用 query 时,是没法获取到 Shadow DOM 里边的元素的,需要在 Shadow Root 上做 query 才行。

在这里附上一个文档,里边有详细的关于新的标准和现在 blink 引擎实现的 Shadow DOM 的区别,官方上称之为 v0 和 v1:Shadow DOM v1 in Blink。

  • '"';
    else
    var content = node.attributes[a].nodeValue;
    html += '>'; html += content; html += '</' + node.nodeName + '>'; break;
    } break;
    case Node.TEXT_NODE: html += node.nodeValue; break;
    case Node.COMMENT_NODE: html += '<!' + '--' + node.nodeValue + '--'
  • '>'; break;
    }
    return html;
    }
    //-->
    </script>

<p><b>Hello</b></p><p><b>cruel</b></p><p><b>World</b></p>

wrapInner(elem)
将每一个匹配的元素的子内容(包括文本节点)用DOM元素包裹起来


Wrap the inner child contents of each matched element (including text nodes) with a DOM element.
返回值
jQuery

参数
elem (Element) : 用于包装目标元素的DOM元素

示例
把所有段落内的每个子内容加粗

HTML 代码:

<p>Hello</p><p>cruel</p><p>World</p>
jQuery 代码:

$("p").wrapInner(document.createElement("b"));
结果:

合并网络请求

Vulcanize 能将多个HTML文件合并成一个文件,从而减少了网络连接数。你可以借助npm安装它,并且用命令行来使用它。你可能也在用 grunt和gulp 托管一些任务,这样的话你可以把vulcanize作为构建过程的一部分。

为了解析依赖以及合并index.html中的导入文件,使用如下命令:

JavaScript

$ vulcanize -o vulcanized.html index.html

1
$ vulcanize -o vulcanized.html index.html

通过执行这个命令,index.html中的依赖会被解析,并且会产生一个合并的HTML文件,称作 vulcanized.html。学习更多有关vulcanize的知识,请看这儿。

注意:http2的服务器推送功能被考虑用于以后消除文件的连结与合并。

二、HTML import

有了自定义元素,就可以写出语义性非常好的 HTML 代码。

XHTML

<share-buttons> <social-button type="weibo"> <a href="...">微博</a> </social-button> <social-button type="weixin"> <a href="...">微信</a> </social-button> </share-buttons>

1
2
3
4
5
6
7
8
<share-buttons>
  <social-button type="weibo">
    <a href="...">微博</a>
  </social-button>
  <social-button type="weixin">
    <a href="...">微信</a>
  </social-button>
</share-buttons>

上面的代码,一眼就能看出语义。

如果将`元素的样式与脚本,封装在一个 HTML 文件share-buttons.html`之中,这个元素就可以复用了。

使用的时候,先引入share-buttons.html

<link rel="import" href="share-buttons.html">

1
<link rel="import" href="share-buttons.html">

然后,就可以在网页中使用``了。

XHTML

<article> <h1>Title</h1> <share-buttons/> ... ... </article>

1
2
3
4
5
<article>
  <h1>Title</h1>
  <share-buttons/>
  ... ...
</article>

HTML imports 的更多用法可以参考教程(1,2)。目前只有 Chrome 浏览器支持这个语法。

Shadow DOM

复制代码 代码如下:

Paragraph.</p></div><div id="content"></div>

wrapAll(html)
将所有匹配的元素用单个元素包裹起来
这于 '.wrap()' 是不同的,'.wrap()'为每一个匹配的元素都包裹一次。
这种包装对于在文档中插入额外的结构化标记最有用,而且它不会破坏原始文档的语义品质。

这个函数的原理是检查提供的第一个元素并在它的代码结构中找到最上层的祖先元素--这个祖先元素就是包装元素。


Wrap all the elements in the matched set into a single wrapper element.
This is different from '.wrap()' where each element in the matched set would get wrapped with an element.
This wrapping process is most useful for injecting additional structure into a document, without ruining the original semantic qualities of a document.

This works by going through the first element provided (which is generated, on the fly, from the provided HTML) and finds the deepest ancestor element within its structure -- it is that element that will enwrap everything else.
返回值
jQuery

参数
html (String) : TML标记代码字符串,用于动态生成元素并包装目标元素

示例
用一个生成的div将所有段落包裹起来

HTML 代码:

<p>Hello</p><p>cruel</p><p>World</p>
jQuery 代码:

$("p").wrapAll("<div></div>");
结果:

关于作者:XfLoops

必威 3

新浪微博:@XfLoops 个人主页 · 我的文章 · 10

必威 4

HTML 自定义元素教程

2017/06/22 · HTML5 · 自定义元素

原文出处: 阮一峰   

组件是 Web 开发的方向,现在的热点是 JavaScript 组件,但是 HTML 组件未来可能更有希望。

本文就介绍 HTML 组件的基础知识:自定义元素(custom elements)。

必威 5

一、浏览器处理

我们一般都使用标准的 HTML 元素。

XHTML

<p>Hello World</p>

1
<p>Hello World</p>

上面代码中,``

``就是标准的 HTML 元素。

如果使用非标准的自定义元素,会有什么结果?

XHTML

<greeting>Hello World</greeting>

1
<greeting>Hello World</greeting>

上面代码中,`就是非标准元素,浏览器不认识它。这段代码的[运行结果](http://jsbin.com/rifozonomu/edit?html,output)是,浏览器照常显示Hello World`,这说明浏览器并没有过滤这个元素。

必威 6

现在,为自定义元素加上样式。

JavaScript

greeting { display: block; font-size: 36px; color: red; }

1
2
3
4
5
greeting {
  display: block;
  font-size: 36px;
  color: red;
}

运行结果如下。

必威 7

接着,使用脚本操作这个元素。

JavaScript

function customTag(tagName, fn){ Array .from(document.getElementsByTagName(tagName)) .forEach(fn); } function greetingHandler(element) { element.innerHTML = '你好,世界'; } customTag('greeting', greetingHandler);

1
2
3
4
5
6
7
8
9
10
11
function customTag(tagName, fn){
  Array
    .from(document.getElementsByTagName(tagName))
    .forEach(fn);
}
 
function greetingHandler(element) {
  element.innerHTML = '你好,世界';
}  
 
customTag('greeting', greetingHandler);

运行结果如下。

必威 8

这说明,浏览器对待自定义元素,就像对待标准元素一样,只是没有默认的样式和行为。这种处理方式是写入 HTML5 标准的。

“User agents must treat elements and attributes that they do not understand as semantically neutral; leaving them in the DOM (for DOM processors), and styling them according to CSS (for CSS processors), but not inferring any meaning from them.”

上面这段话的意思是,浏览器必须将自定义元素保留在 DOM 之中,但不会任何语义。除此之外,自定义元素与标准元素都一致。

事实上,浏览器提供了一个HTMLUnknownElement对象,所有自定义元素都是该对象的实例。

JavaScript

var tabs = document.createElement('tabs'); tabs instanceof HTMLUnknownElement // true tabs instanceof HTMLElement // true

1
2
3
4
var tabs = document.createElement('tabs');
 
tabs instanceof HTMLUnknownElement // true
tabs instanceof HTMLElement // true

上面代码中,tabs是一个自定义元素,同时继承了HTMLUnknownElementHTMLElement接口。

Custom Elements

firefox没有outerHTML用以下方法解决


HTML imports 入门

2015/02/10 · HTML5 · HTML, imports

本文由 伯乐在线 - XfLoops 翻译,周进林 校稿。未经许可,禁止转载!
英文出处:webcomponents.org。欢迎加入翻译组。

Template、Shadow DOM及Custom Elements 让你创建UI组件比以前更容易了。但是像HTML、CSS、JavaScript这样的资源仍然需要一个个地去加载,这是很没效率的。

删除重复依赖也并不简单。例如,现在加载jQuery UI或Bootstrap就需要为JavaScript、CSS及Web Fonts添加单独的标签。如果你的Web 组件应用了多重的依赖,那事情就变得更为复杂。

HTML 导入让你以一个合并的HTML文件来加载这些资源。

三、Custom Elements 标准

HTML5 标准规定了自定义元素是合法的。然后,W3C 就为自定义元素制定了一个单独的 Custom Elements 标准。

它与其他三个标准放在一起—- HTML Imports,HTML Template、Shadow DOM—-统称为 Web Components 规范。目前,这个规范只有 Chrome 浏览器支持。

必威 9

Custom Elements 标准对自定义元素的名字做了限制。

“自定义元素的名字必须包含一个破折号(-)所以都是正确的名字,而和``是不正确的。这样的限制使得 HTML 解析器可以分辨那些是标准元素,哪些是自定义元素。”

必威 10

注意,一旦名字之中使用了破折号,自定义元素就不是HTMLUnknownElement的实例了。

JavaScript

var xTabs = document.createElement('x-tabs'); xTabs instanceof HTMLUnknownElement // false xTabs instanceof HTMLElement // true

1
2
3
4
var xTabs = document.createElement('x-tabs');
 
xTabs instanceof HTMLUnknownElement // false
xTabs instanceof HTMLElement // true

Custom Elements 标准规定了,自定义元素的定义可以使用 ES6 的class语法。

JavaScript

// 定义一个 class MyElement extends HTMLElement {...} window.customElements.define('my-element', MyElement);

1
2
3
// 定义一个
class MyElement extends HTMLElement {...}
window.customElements.define('my-element', MyElement);

上面代码中,原生的window.customElements对象的define方法用来定义 Custom Element。该方法接受两个参数,第一个参数是自定义元素的名字,第二个参数是一个 ES6 的class

这个class使用getset方法定义 Custom Element 的某个属性。

JavaScript

class MyElement extends HTMLElement { get content() { return this.getAttribute('content'); } set content(val) { this.setAttribute('content', val); } }

1
2
3
4
5
6
7
8
9
class MyElement extends HTMLElement {
  get content() {
    return this.getAttribute('content');
  }
 
  set content(val) {
    this.setAttribute('content', val);
  }
}

有了这个定义,网页之中就可以插入``了。

JavaScript

<my-element content="Custom Element"> Hello </my-element>

1
2
3
<my-element content="Custom Element">
  Hello
</my-element>

处理脚本如下。

JavaScript

function customTag(tagName, fn){ Array .from(document.getElementsByTagName(tagName)) .forEach(fn); } function myElementHandler(element) { element.textConent = element.content; } customTag('my-element', myElementHandler);

1
2
3
4
5
6
7
8
9
10
11
function customTag(tagName, fn){
  Array
    .from(document.getElementsByTagName(tagName))
    .forEach(fn);
}
 
function myElementHandler(element) {
  element.textConent = element.content;
}
 
customTag('my-element', myElementHandler);

运行结果如下。

必威 11

ES6 Class 的一个好处是,可以很容易地写出继承类。

JavaScript

class MyNewElement extends MyElement { // ... } customElements.define('my-new-element', MyNewElement);

1
2
3
4
5
class MyNewElement extends MyElement {
  // ...
}
 
customElements.define('my-new-element', MyNewElement);

今天的教程就到这里,更多用法请参考谷歌的官方教程。

阻塞式

我们要知道的是,默认的 link 加载是阻塞式的,除非你给他添加一个 async 标识。

阻塞式从某种程度上讲是有必要的,当你 improt 的是一个完整的自定义组件并且需要在主 HTML 中用标签直接使用时,非阻塞的就会出现错误了,因为标签还没有被注册。

//---在组件最后插入html代码
function InsertHtm(op,code,isStart){
if(Dvbbs_IsIE5)
op.insertAdjacentHTML(isStart ? "afterbegin" : "afterEnd",code);
else{
var range=op.ownerDocument.createRange();
range.setStartBefore(op);
var fragment = range.createContextualFragment(code);
if(isStart)
op.insertBefore(fragment,op.firstChild);
else
op.appendChild(fragment);
}
}

$("p").wrap("<div class='wrap'></div>");
结果:

性能方面的考虑

使用HTML 导入的一个好处是能够将资源组织起来,但是也意味着在加载这些资源的时候,由于使用了一些额外的HTML文件而让头部变得过大。有几点是需要考虑的:

概述

这个东西很简单,用过 handlebars 的人都知道有这么一个东西:

XHTML

<script id="template" type="text/x-handlebars-template"> ... </script>

1
2
3
<script id="template" type="text/x-handlebars-template">
  ...
</script>

其他模板引擎也有类似的东西,那么 HTML Templates 便是把这个东西官方标准化,提供了一个 template 标签来存放以后需要但是暂时不渲染的 HTML 代码。

以后可以这么写了:

XHTML

<template id="template"> ... </template>

1
2
3
<template id="template">
  ...
</template>

代码说明:
1 代码中首先条件判断来监测浏览器是否支持outerHTML以避免覆盖浏览器原生的实现。
2 "__defineSetter__","__defineGetter__" 是firefox浏览器私有方面。分别定义当设置属性值和获取属性要执行的操作。
3 在"__defineSetter__" "outerHTML"中为了避免插入页面中元素过多导致频繁发生reflow影响性能。使用了文档碎片对象fragment来暂存需要插入页面中DOM元素。
4 在"__defineGetter__" "outerHTML" 中使用元素attributes属性来遍历给元素指定的属性。结合innerHTML返回了包含原属本身在内的html字符串。
测试代码:

Wrap all matched elements with a structure of other elements.
This wrapping process is most useful for injecting additional structure into a document, without ruining the original semantic qualities of a document.
This works by going through the first element provided (which is generated, on the fly, from the provided HTML) and finds the deepest ancestor element within its structure -- it is that element that will enwrap everything else.

把Template、Shadow DOM、自定义元素跟HTML导入结合起来

让我们对这个文章系列的代码使用HTML导入。你之前可能没有看过这些文章,我先解释一下:Template可以让你用声明的方式定义你的自定义元素的内容。Shadow DOM可以让一个元素的style、ID、class只作用到其本身。自定义元素可以让你自定义HTML标签。通过把这些跟HTML导入结合起来,你自定义的web 组件会变得模块化,具有复用性。任何人添加一个Link标签就可以使用它。

x-component.html

XHTML

<template id="template"> <style> ... </style> <div id="container"> <img src="; <content select="h1"></content> </div> </template> <script> // This element will be registered to index.html // Because `document` here means the one in index.html var XComponent = document.registerElement('x-component', { prototype: Object.create(HTMLElement.prototype, { createdCallback: { value: function() { var root = this.createShadowRoot(); var template = document.querySelector('#template'); var clone = document.importNode(template.content, true); root.appendChild(clone); } } }) }); </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template id="template">
  <style>
    ...
  </style>
  <div id="container">
    <img src="http://webcomponents.org/img/logo.svg">
    <content select="h1"></content>
  </div>
</template>
<script>
  // This element will be registered to index.html
  // Because `document` here means the one in index.html
  var XComponent = document.registerElement('x-component', {
    prototype: Object.create(HTMLElement.prototype, {
      createdCallback: {
        value: function() {
          var root = this.createShadowRoot();
          var template = document.querySelector('#template');
          var clone = document.importNode(template.content, true);
          root.appendChild(clone);
        }
      }
    })
  });
</script>

index.html

XHTML

... <link rel="import" href="x-component.html"> </head> <body> <x-component> <h1>This is Custom Element</h1> </x-component> ...

1
2
3
4
5
6
7
8
...
  <link rel="import" href="x-component.html">
</head>
<body>
  <x-component>
    <h1>This is Custom Element</h1>
  </x-component>
  ...

注意,因为x-component.html 中的document 对象跟index.html的一样,你没必要再写一些棘手的代码,它会自动为你注册。

TAG标签:
版权声明:本文由必威发布于必威-前端,转载请注明出处:原文出处必威:,不过在DOM标准中并没有定义o