windows标准组件调用
在这个由三部分组成的系列的前两篇文章中,您学习了如何实现HTML5临时滑块组件。 该组件提供了许多复杂的功能。 例如,当用户单击滑块的轨道时,它使用CSS3过渡平滑地滑动滑块的旋钮。 但是(从系列的第一篇文章中可以看到),从开发人员的角度来看,在应用程序中使用内置HTML元素并不那么简单。
要使用临时滑块,您必须:
- 在您HTML中添加一个空的
DIV
。 - 创建一个新的
COREHTML5.Slider
。 - 将在步骤2中创建的滑块追加到在步骤1中创建的空
DIV
中。 - 实现事件处理程序以将控件连接到滑块。
除了难以使用之外,临时滑块还容易受到滥用,无论是否故意。 任何人都可以访问该滑块的元素,因为-与当今几乎所有其他所有JavaScript即席组件一样-滑块将它们直接添加到DOM树中。
减少前面的步骤以简单地向HTML添加一个Slider标签将是理想的。 这就是您将通过使用以下API将即席滑块变成标准HTML5组件来完成的操作:
- Shadow DOM :Shadow DOM API使您可以将通过常规方式无法访问的元素添加到DOM树中,例如
document.getElementById()
。 Shadow DOM中的元素在阴影中潜伏; 它们对于DOM树的其余部分实际上是不可见的,并且默认情况下,它们不受周围文档中CSS的影响。 - 模板 :HTML5模板是惰性文档片段。 当您需要显示该片段时,您可以标记 ( 创建 )模板,从而使该片段在DOM树中生效。 模板使您可以封装可以标记和定制HTML,JavaScript和CSS。
- 自定义元素 :自定义元素是组件的标签。 它们与内置HTML标记没有区别,但自定义组件的标记必须包含连字符(例如
<custom-tag>
)。 - HTML导入 :HTML导入使您可以从另一个导入一个HTML文件,这使您可以在其自己的文件中实现自定义组件。
这些API仍在开发中。 在撰写本文时,还没有浏览器支持它们全部。 因此,要立即开始实施HTML5标准组件,可以从提供缺少功能的两个开源项目中进行选择:Polymer和X-Tags。
本文向您展示如何:
- 将组件标记封装在Shadow DOM中。
- 从惰性HTML模板中删除实时DOM元素。
- 创建自定义元素并将其连接到JavaScript。
- 导入HTML文件。
- 用Polymer实现组件。
- 使用X标签实现组件。
影子DOM
图1显示了播放视频HTML5应用程序以及Chrome的Elements检查器(位于Tools-> Developer tools下 ),该检查器显示了video
元素内的元素:
图1. video
元素内部
请注意,即使很明显, video
元素包含播放/暂停按钮,进度指示器和其他元素,元素检查器也仅在video
元素内显示单个source
元素。 您看不到它们,因为它们位于Shadow DOM中,默认情况下不可见。 图2显示了如何在Chrome的Elements检查器中显示Shadow DOM元素:
图2.在Chrome中启用影子DOM
要进入图2所示的设置对话框,请点击Chrome开发者工具窗口右下角的齿轮图标。 向下滚动到Show Shadow DOM复选框。 选中该框后,Chrome的Elements检查器会在阴影DOM中显示元素,如图3所示:
图3.查看video
元素的影子DOM
与图1相似, 图3显示了video
元素内部的元素,但是这次由于Chrome正在显示Shadow DOM元素,因此video
元素的内部可见。
Shadow DOM(直到最近才仅对浏览器实现者可用)对应用程序开发人员很有用,因为默认情况下,Shadow DOM元素不可被周围的DOM树访问且不受其影响。 因为它们本质上封装在自己的世界中,所以其他组件不会干扰它们的工作方式 。
图4显示了一个人为设计示例的结果,该示例说明了如何将元素放入Shadow DOM:
图4.更改按钮的影子根
该应用程序的启动类似于图4左侧的屏幕截图,其中仅显示标题和按钮。 如果单击按钮,则应用程序将使用位于按钮的Shadow DOM中的图片和标题(如右图所示)替换文本。
清单1显示了图4所示的应用程序HTML:
清单1.填充按钮的Shadow DOM
<!DOCTYPE html>
<html>
<head>
<title>Shadow DOM</title>
<style>
button {
font: 18px Century Schoolbook;
border: thin solid gray;
background: rgba(200, 200, 200, 0.5);
padding: 10px;
}
</style>
</head>
<body>
<h1>Shadow DOM</h1>
<button id='button'>Click this button</button>
<script>
var b = document.getElementById('button');
b.onclick = function (e) {
var sr = b.webkitCreateShadowRoot();
sr.innerHTML = '<p>This content is in the shadow DOM</p>' +
'<img src="beach.png">';
};
</script>
</body>
</html>
当应用程序在浏览器中加载时, 清单1底部JavaScript运行,创建一个onclick
事件处理程序,该事件处理程序使用webkitCreateShadowRoot()
方法创建阴影根 ,即阴影DOM树中的顶部节点。 然后,事件处理程序使用两个元素填充该阴影根:一个段落和一个图像。
每当为元素创建影子根目录并随后设置元素的内部HTML时,都将覆盖元素的原始内容, 如图4中的应用程序所示。
默认情况下,按钮没有Shadow DOM,因此浏览器允许您创建一个按钮,如清单1所示 。 但是对于默认情况下已经具有Shadow DOM的元素(例如video
元素),浏览器并不那么适应。 如图5所示, sr = b.webkitCreateShadowRoot();
导致Chrome元素检查器发出不属于该节点的错误消息:
图5.尝试(失败)更改video
元素的阴影根
模板,自定义元素和HTML导入
既然您已经看到了Shadow DOM的实际应用,我将把它与HTML5 Web组件的其他主要组成部分一起实用地使用:模板,自定义元素和HTML导入。
图6显示了HTML5丹佛聚会组中的聚会的简单无序列表:
图6.聚会的无序列表
清单2显示了在图6中创建无序列表的标记:
清单2.无序聚会HTML
<!DOCTYPE html>
<html>
<head>
<title>Meetups Component</title>
</head>
<body>
<ul>
<li class="mobile">Sencha Touch.
<span class='date'>Jan 23</span></li>
<li class="html5-in-general">HTML5 & Semantic Markup.
<span class='date'>Jun 18</span></li>
<li class="html5-in-general">HTML5 & Windows 8.
<span class='date'>Jul 23</span></li>
<li class="javascript">HTML5 JavaScript on Crack.
<span class='date'>Aug 20</span></li>
<li class="javascript">CoffeeScript for Recovering JavaScript Programmers.
<span class='date'>Sept 17</span></li>
<li class="html5-in-general">ClojureScript and CouchDB.
<span class='date'>May 21</span></li>
<li class="html5-in-general">Polyfills for the Pragmatist.
<span class='date'>Apr 23</span></li>
<li class="design">CSS3 for Programmers.
<span class='date'>Feb 20</span></li>
<li class="lightning">Quick-start web apps;
Graphics; Data visualization; Adaptive patterns; JSON.
<span class='date'>Oct 22</span></li>
<li class="lightning">Web workers; CSS3; HTTP; Audio, video, & canvas;
Charting; JS evolution.
<span class='date'>March 19</span></li>
</ul>
</body>
</html>
清单2HTML没什么变化。 它只是创建带有列表项的单个无序列表。 图7显示了相同的列表项,但不是驻留在无序列表中,而是位于自定义组件内部并由其操作:
图7.一个定制的meetup列表组件
清单3显示了图7所示的应用程序的标记:
清单3. Meetup-talks组件而不是无序列表
<!DOCTYPE html>
<html>
<head>
<title>Meetups Component</title>
<script src="../polymer/polymer.js"></script>
<link rel="components" href="meetup-component.html">
</head>
<body>
<meetup-talks>
<li class="mobile">Sencha Touch.
<span class='date'>Jan 23</span></li>
<li class="html5-in-general">HTML5 & Semantic Markup.
<span class='date'>Jun 18</span></li>
<li class="html5-in-general">HTML5 & Windows 8.
<span class='date'>Jul 23</span></li>
<li class="javascript">HTML5 JavaScript on Crack.
<span class='date'>Aug 20</span></li>
<li class="javascript">CoffeeScript for Recovering JavaScript Programmers.
<span class='date'>Sept 17</span></li>
<li class="html5-in-general">ClojureScript and CouchDB.
<span class='date'>May 21</span></li>
<li class="html5-in-general">Polyfills for the Pragmatist.
<span class='date'>Apr 23</span></li>
<li class="design">CSS3 for Programmers.
<span class='date'>Feb 20</span></li>
<li class="lightning">Quick-start web apps;
Graphics; Data visualization; Adaptive patterns; JSON.
<span class='date'>Oct 22</span></li>
<li class="lightning">Web workers; CSS3; HTTP; Audio, video, & canvas;
Charting; JS evolution.
<span class='date'>March 19</span></li>
</meetup-talks>
</body>
</html>
- 清单3中的标记包括一个名为polymer.js的文件。 该文件与Polymer开源项目一起提供,该项目提供HTML 5 Web组件API(阴影DOM,模板等)的实现。 我将在下一节的“ 聚合物”部分中详细讨论的“聚合物”项目由Chrome开发团队实施。
- 清单3使用
link
元素导入另一个HTML5文件。 对link
元素的使用被称为HTML import 。 在撰写本文时,还没有任何浏览器实现HTML导入。 在此示例中,该功能来自Polymer项目。 - 清单2中的无序列表元素替换为清单3中的
meetup-talks
定制元素。
meetup-talks
定制元素在metup-component.html中实现,如清单4所示:
清单4. meetup-list
元素
<element name="meetup-list">
<template>
<style>
/* styles that follow only apply to the ShadowDOM of the <meetup-list> element */
#meetups {
display: block;
padding: 15px;
padding-top: 0px;
background: lightgray;
border: thin solid cornflowerblue;
}
.title {
color: blue;
font-size: 1.5em;
}
</style>
<div id='meetups'>
<p class='title'>HTML5 in General</p>
<content select='.html5-in-general'></content>
<p class='title'>JavaScript</p>
<content select='.javascript'></content>
<p class='title'>Design</p>
<content select='.design'></content>
<p class='title'>Lightning</p>
<content select='.lightning'></content>
</div>
</template>
<script>
Polymer.register(this);
</script>
</element>
清单4是使事情变得有趣的地方。 首先,我声明一个名称为meetup-list
的新元素,并小心地在HTML规范要求的名称中包括一个连字符。 在清单的最后,一行JavaScript向Polymer
全局对象注册了meetup-list
元素。 这需要声明和注册自定义元素,以便可以在HTML页面中使用它。
在element
元素内部,我声明一个template
。 模板以声明方式而非编程方式定义了Shadow DOM。 因此,在清单4中 ,我创建了一个Shadow DOM模板。 每当有人在HTML页面中使用meetup-list
元素时,浏览器就会使用该Shadow DOM模板为该元素标记(创建)新的Shadow DOM实例。
在模板内部,我定义了两种CSS样式,它们仅适用于template中的元素 。 反过来说,由于模板代表了Shadow DOM,因此在周围文档中定义CSS样式不会影响模板中的元素 。 例如,如果要为清单3中HTML添加段落元素的样式,则该样式不会影响模板中的段落。
除了CSS样式外,模板还定义了随后的Shadow DOM实例中的元素。 content
元素使用CSS选择器从meetup-list
原始内容中选择列表项。 因此,模板除了以声明方式指定Shadow DOM实例外,还可以有选择地将元素的原始内容插入模板本身 。 请注意,您可以使用<content></content>
将元素的全部原始内容插入其模板。
立即实施Web组件
我在本文中讨论的所有Web组件API都是相对较新的,浏览器供应商之间提供了不同程度的支持。 为了今天有效地实现组件,您不仅可以编写代码并将其加载到浏览器中; 相反,您需要HTML5业务中称为polyfills或shims的解决方案:可以使用新功能的解决方案(如果可用),而不能使用新功能。
当前,有两个项目提供Web组件polyfill:Polymer和Mozilla X-Tags。 Polymer仅适用于Chrome,并且要求网络服务器使用HTML导入,因此不适合生产使用。 X-Tags几乎可以与所有现代浏览器一起使用,并且不需要Web服务器。 但是,X-Tags仅实现自定义元素API,而没有模板和Shadow DOM。
接下来,我向您展示如何分别使用Polymer和X-Tags将标准组件包装在我在本系列的前几篇文章中实现的即席滑块周围。
聚合物
根据定义,临时组件不遵循组件标准,因此没有实现或使用它们的标准方法。 但是,通过将它们包装在标准组件中,可以使它们更易于使用。 首先,我使用Polymer将临时滑块包装在标准组件中。 图8显示了Polymer版本的滑块:
图8.聚合物滑块
清单5显示了如何使用Polymer版本的滑块:
清单5.使用Polymer滑块标签
<!DOCTYPE html>
<html>
<head>
<title>Slider with Polymer</title>
<script src="../polymer/polymer.js"></script>
<script src="lib/slider.js"></script>
<link rel="import" href="./slider.html" />
</head>
<body>
<x-slider id='slider'
fillColor='goldenrod'
strokeColor='red'>
</x-slider>
</body>
</html>
如果将本系列的第一篇文章中的清单5与清单2进行比较,它展示了如何单独使用临时滑块,那么您会发现清单5非常简单。 使用x-slider
聚合物元素,您可以使用一行HTML创建一个滑块。
清单6中实现了x-slider element
:
清单6. Polymer滑块组件
<element name="x-slider" attributes="strokeColor fillColor">
<template>
<style>
x-slider {
display: block;
}
.x-slider-button {
float: left;
margin-left: 2px;
margin-top: 5px;
margin-right: 5px;
vertical-align: center;
border-radius: 4px;
border: 1px solid rgb(100, 100, 180);
background: rgba(255, 255, 0, 0.2);
box-shadow: 1px 1px 4px rgba(0,0,0,0.5);
cursor: pointer;
width: 25px;
height: 20px;
}
.x-slider-button:hover {
background: rgba(255, 255, 0, 0.4);
}
#xSliderDiv {
position: relative;
float: right;
width: 80%;
height: 65%;
}
</style>
<div id='x-slider-buttons-div'
style='width: {{width}}px; height: {{height}}px;'>
<button class='x-slider-button' id='xSliderMinusButton'>-</button>
<button class='x-slider-button' id='xSliderPlusButton'/>+</button>
<div id='xSliderDiv'></div>
</div>
</template>
<script>
Polymer.register(this, {
width: 350,
height: 50,
strokeColor: 'blue',
fillColor: 'cornflowerblue',
ready: function () {
var self = this,
slider = new COREHTML5.Slider(this.strokeColor,
this.fillColor, 0);
setTimeout( function () { // This timeout is a hack
slider.appendTo(self.$.xSliderDiv);
slider.draw();
}, 200);
this.$.xSliderMinusButton.onclick = function () {
if (slider.knobPercent >= 0.1) {
slider.knobPercent -= 0.1;
slider.erase();
slider.draw();
}
};
this.$.xSliderPlusButton.onclick = function () {
if (slider.knobPercent <= 0.9) {
slider.knobPercent += 0.1;
slider.erase();
slider.draw();
}
};
}
});
</script>
</element>
清单6与清单4相似。 这两个清单都定义了一个新元素,并将该元素注册到Polymer全局对象。 两者都定义了一个包含CSS样式和标记的模板。 清单6与清单4的区别在于清单6对Polymer数据绑定和ready
事件处理程序的使用。
双胡子符号(如{{width}}
符号)表示元素属性。 您可以在元素的标记中使用双胡子符号。 在清单6中 ,我将元素包围的DIV
的宽度和高度设置为页面作者使用元素属性设置的宽度和高度。
您可以在传递给Polymer.register()
方法的对象中声明属性; 例如, 清单6为x-slider
元素声明了四个属性: width
, height
, strokeColor
和fillColor
。 如果使用element
元素的attributes
属性指定属性(称为发布属性) ,则页面作者可以在其元素中使用这些属性,如清单5中的x-slider
元素一样。
根据Polymer文档, 当组件完成对自身的初始化后 ,如果存在,它将调用其ready()
方法。 该文档没有进一步详细说明组件何时进行初始化,而是显然是在浏览器准备绘制组件之前。 由于组件在绘制之前就已初始化,并且因为ready()
方法是当前生命周期中的唯一方法,所以我不得不添加一个setTimeout()
hack,该方法在ready()
方法之后200ms绘制滑块被调用。 这样的粗糙边缘无疑会随着时间的流逝而平滑。 (Web组件规范还定义了一个inserted
回调,浏览器在将元素插入DOM之后会被浏览器调用,但是在发布本文时,Polymer不支持该回调。)
Polymer还为自定义元素提供了$
属性,该属性引用了元素属性的映射。 我使用该地图访问最终包含临时滑块的DIV
。
X标签
滑块的X-Tags版本如图9所示:
图9. X-Tags滑块组件
滑块的Polymer和X-Tags实现在视觉上是无法区分的。 我用不同的颜色实现它们只是为了说明它们是用不同的框架实现的。 清单7显示了图9所示的应用程序如何使用滑块的X-Tags版本(也实现为x-slider
):
清单7.使用X-Tags滑块组件
<!DOCTYPE html>
<html>
<head>
<title>Slider with x-tag</title>
<link rel="stylesheet" type="text/css" href="x-slider.css" />
</head>
<body>
<x-slider id='slider'
slider-fill-color='rgba(50, 105, 200, 0.8)'
slider-stroke-color='navy'>
</x-slider>
</body>
<script type="text/javascript" src="lib/x-tag.js"></script>
<script type="text/javascript" src="lib/slider.js"></script>
<script type="text/javascript" src="x-slider.js"></script>
</html>
清单7与清单5相似。 这两个清单都使用一个简单的x-slider
元素在页面中放置一个滑块。 区别在于Polymer版本使用HTML导入来包含定义滑块HTML片段,而X-Tags版本则包含JavaScript,因为它不支持HTML导入。
清单8显示了实现该组件JavaScript:
清单8.滑块JavaScript
function getFirstAncestor(name, startingElement) {
var element = startingElement
, localName = element.localName;
while (localName !== name) {
element = element.parentNode;
localName = element.localName;
}
return element;
};
function getSlider(element) {
return getFirstAncestor('x-slider', element).slider;
};
xtag.register('x-slider', {
onCreate: function () {
var content =
"<div id='x-slider-buttons-div'>" +
"<button class='x-slider-button' id='x-slider-minus-button'>-</button>" +
"<button class='x-slider-button' id='x-slider-plus-button'>+</button>" +
"</div>" +
"" +
"<div id='x-slider-slider-div'></div>" +
"" +
"<div id='x-slider-readout-div'></div>";
stroke = this.getAttribute('slider-stroke-color'),
fill = this.getAttribute('slider-fill-color');
// 'this' is the x-slider HTML element
this.max = 100;
this.innerHTML = content;
this.slider = new COREHTML5.Slider(stroke, fill, 0);
this.slider.appendTo('x-slider-slider-div');
this.slider.draw();
},
events: {
'click:touch:delegate(#x-slider-plus-button)': function(event, slider) {
var slider = getSlider(this) // 'this' is the button
, value = getFirstAncestor('x-slider', this).getValue();
if (slider.knobPercent <= 0.9) {
slider.knobPercent += 0.1;
}
slider.erase();
slider.draw();
console.log(value);
},
'click:touch:delegate(#x-slider-minus-button)': function(event, slider) {
var slider = getSlider(this) // 'this' is the button
, value = getFirstAncestor('x-slider', this).getValue();
if (slider.knobPercent >= 0.1) {
slider.knobPercent -= 0.1;
}
slider.erase();
slider.draw();
console.log(value);
},
},
methods: {
getValue: function () {
// 'this' is the x-slider HTML element
return this.slider.knobPercent * this.max;
}
}
});
清单8中用X-Tags定义组件与清单6中用Polymer定义组件完全不同。 X-Tags纯粹在JavaScript中定义元素,这涉及使用字符串连接并设置元素的内部HTML来设置自定义元素的内容。 与Polymer形成鲜明对比的是Polymer,它以更易读和可维护HTML定义了自定义元素的内容。
X-Tags没有像Polymer的$
这样的属性,可以轻松访问自定义元素中的元素,因此我必须实现getFirstAncestor()
函数来访问包含临时滑块的元素。
最后,X-Tags支持实现事件以处理控制滑块旋钮的按钮的单击。 但是,对于本次练习而言,这种支持对我而言似乎是过大的。
为了完整起见,清单9中显示了滑块的X-Tags版本CSS:
清单9.滑块CSS
x-slider {
display: block;
}
.x-slider-button {
float: left;
margin-left: 2px;
margin-top: 15px;
margin-right: 5px;
vertical-align: center;
border-radius: 4px;
border: 1px solid rgb(100, 100, 180);
background: rgba(255, 255, 0, 0.2);
box-shadow: 1px 1px 4px rgba(0,0,0,0.5);
cursor: pointer;
width: 25px;
height: 20px;
}
.x-slider-button:hover {
background: rgba(255, 255, 0, 0.4);
}
#x-slider-buttons-div {
width: 25%;
height: 100%;
}
#x-slider-slider-div {
position: relative;
float: right;
margin-top: -40px;
width: 80%;
height: 65%;
}
结论
当前,JavaScript框架主要实现临时组件,这些组件遵循专有API而非标准。 结果,每当您迁移到新框架时,您都将面临巨大的学习曲线。 而且由于临时组件通常没有被封装,因此其功能可能会受到应用程序中其他组件的不利影响。
在本系列文章中 ,您已经了解了如何使用HTML5 API来实现标准组件和即席组件。 用于实现组件HTML5规范是相对较新的,浏览器供应商之间的支持也不同。 您可以立即开始实施标准组件,但是您需要的不仅仅是浏览器。 在本文中,我简要讨论了两个框架,即Polymer和X-Tag,它们使实现标准组件成为可能。
随着Web组件规范的成熟以及浏览器供应商实现其功能,我们很快将拥有一个可行的平台来构建标准组件。 然后,实现HTML5应用程序将比今天更加有趣。
翻译自: https://www.ibm.com/developerworks/web/library/wa-html5components3/index.html
windows标准组件调用