大家好,又见面了,我是你们的朋友全栈君。
Widget类的结构和职责
Widget类是一个用于创建widgets的基础类。 Widget类可以实例化,但是一般都是用它作为基础类,扩展创建widgets,这些通过扩展创建的widgets上有特定的用户交互模式。
Widget类扩展Base类。因此,它像Base类一样提供Attribute、Event.provider、Plugin.host功能。除了这些功能以外,widget类还增加了以下核心功能。
Widget类引入一组属性,每一个Widget类的实例都能使用这些属性:boundingBox、contentBox、width、height、visible、focused、disabled。
Widget类在Base类的init方法和destroy方法的基础上增加了渲染方法(和事件)。
Widget类定义抽象的方法:renderUI、bindUI、syncUI,为widgets实例的渲染提供统一的入口。
Widget类在初始化的过程中,为渐进增强提供统一的入口。它还提供一个机制,来避免渐进增强HTML代码无样式闪动。
Widget的strings属性提供了字符串本地化支持。当和internationalization功能一起使用时,可以把需要本地化的字符串与核心代码分离。
Widget类创建了一系列属性,这些属性在所有的widget中都能使用,以下是详细描述:
属性 | 描述 |
---|---|
boundingBox | widget的外层节点。用以定位和调整大小。还可以用作皮肤HTML代码的容器 |
contentBox | boundingBox的子节点,包含widget的所有内容。一般这是体现widget外观感觉的节点 |
srcNode | 页面上的一个节点。在创建widget时,如果需要渐进增强地使用页面中的标签代码,开发者需要提供这个节点 |
tabIndex | 应用于boundingBox的tabIndex。 |
focused | 一个标识。标明widget当前是否是获得焦点。widget类为boundingBox标记一个”focused”class名。除此之外,获得焦点的其他操作都是在相应的widget中实现。 |
disabled | 标明widget当前是否可用。widget类为boundingBox增加一个“disabled”class名。除此之外,disabled的其他操作都是在相应的widget中实现。 |
visible | 一个标识。标明widget当前是否可见。Widget类为boundingBox增加一个“hidden”class名。除此以外,隐藏的实现在相应的widget的CSS中实现(可以通过visibility、display、或者超出屏幕范围的定位来实现)。 |
height | 包含单位的字符串。或者一个数字。代表widget的高度。如果是数字,则使用默认单位。默认单位由DEF_UNIT静态属性定义。高度应用在boundBox上。 |
width | 包含单位的字符串。或者一个数字。代表widget的宽度。如果是数字,则使用默认单位。默认单位由DEF_UNIT静态属性定义。宽度应用在boundBox上。 |
stings | 用来标识widget UI的字符串。 |
Widget类在Base类的init阶段和destroy阶段中加入渲染阶段。渲染方法确定widget铺设UI(通过往DOM中增加元素或者修改元素)的时间点。然后设置监听器来激活该UI。有了这个渲染阶段,widgets类可以把它的状态及相应的逻辑和UI展示分离开来。这样的分离是为了让widgets在安全地修改它的状态以后,再把这些修改体现到DOM元素中。
这样的一个分离的概念,把代码区分成两种方法,一种方法用以处理widget的状态和逻辑,一种用以处理DOM。
和init、destroy一样,Widget类的render方法是final(不能重载)的,该render方法被代理给widget实现的renderer方法。
init方法在类层级中循环执行(从基类到子类):
init方法会触发init事件,阻止这个事件可以停止初始化过程。
调用类层级中的destructor方法,(从子类到基类)。
destroy方法触发destroy事件,阻止destroy事件可以停止销毁(destroy)过程。
调用Widget实例的renderer方法。与initializer和destructor方法不同,这个方法不会在Widget类层级中自动循环。
render方法接收一个parentNode参数,这个参数用来指定页面中的某个元素,在渲染完成后,该widget会被-插到这个元素中。如果contentBox或boundingBox指向的元素不在页面上,而且parentNode未指定任何元素,widget会被-插入页面成为body的第一个子元素。
render方法会触发render事件,阻止这个事件可以停止渲染(render)过程。
Widget类提供renderer方法的实现,对于大部分简单的widget来说,不需要覆盖这个实现。renderer方法的实现如下:
renderer: function() {
this.renderUI();
this.bindUI();
this.syncUI();
}
renderUI,bindUI,syncUI都是抽象方法。用以为widget确立统一的开发模式。这些抽象方法扮演以下角色:
该方法的职责是往页面中创建增加widget需要的HTML节点(或者是改变页面中现有的HTML节点)。通常这是widget对DOM的第一次修改。
该方法的职责是添加事件监听器,将UI的状态和widget的状态关联起来。这些事件监听器一般监听属性的change 事件,响应属性值的变化,改变UI的状态。该方法还负责添加DOM事件监听器,把用户交互和widget的API关联起来。
该方法的职责是在渲染的过程中,基于widget当前的状态设置UI的初始状态。
Widget类为需要渐进增强支持的widget提供了统一入口。这个入口以HTML_PARSER静态属性的形式存于每一个widget中。
HTML_PARSER是一个静态属性,该静态属性中定义了一系列选择器和函数,这些选择器和函数的职责是: a) 从现有的DOM元素中为widget解析内容; b) 在初始化过程中从attribute配置获取值,用于初始化。
MyWidget.HTML_PARSER = {
// Set attributes which are Node references
// using selector syntax (uses node.one())
titleNode: "span.yui3-title",
// Set attributes which are multiple Node references
// using selector syntax (uses node.all())
itemNodes: ["li.yui3-listitem"],
// Set attributes using a parse function. Execution context
// is set to the widget instance
label: function(srcNode) {
return srcNode.one("input.yui3-title").get("innerHTML");
},
xValue: function(srcNode) {
return srcNode.one("input.yui3-slider-xvalue").get("value");
},
yValue: function(srcNode) {
return srcNode.one("input.yui3-slider-yvalue").get("value");
}
};
Widget类不仅通过HTML_PARSER提供统一的渐进增强解决方案入口,还提供了在初始化过程中隐藏渐进增强内容的CSS接口。
为了达到这个目的:
.yui3-js-enabled .yui3-widget-loading {
display: none;
}
.yui3-js-enabled .yui3-overlay-loading {
/* Hide overlay markup offscreen */
position:absolute;
top:-1000em;
left:-1000em;
}
Widget类通过boundingBox、contentBox属性建立统一的HTML标签格式。
大部分的widgets会有由bounding box包着一个content box。两个盒子默认都是DIV。可以通过覆盖BOUNDING_TEMPLATE and CONTENT_TEMPLATE原型属性来修改。如果widget不需要两个嵌套盒子这样的结构,开发者也可以把CONTENT_TEMPLATE设置成null。这样,boundingBox和contentBox属性都指向相同的HTML节点。
如果在构造函数中未提供这两个节点,widget会创建这两个节点,然后再render方法被调用的时候把这两个节点添加到页面中。如果render方法调用时,render方法的参数指向了某个节点,widget的boundingBox会被-插到这个节点中。
bounding box是widget最外层的元素,bounding box的目的是实现功能,而不是实现视觉效果。
bounding box可以有会影响widget文档流的CSS定义。比如盒子类型(“display:inline”, “display:inline-block”, “display:block”)和定位模式(”position:absolute”, “position:relative”)。
content box是bounding box的子节点。widget将构成核心的UI元素添加到content box中。
下图展示widget的HTML标签和class名:
两个嵌套盒子为CSS应用、装饰元素支持、bounding box宽高控制提供方便。下面是详细说明:
为了在所有的widget中提供统一的class名,Widget类提供两个方法,这两个方法打包了ClassNameManager功能,基于widget类的NAME属性生成class名。
此方法可以用于生成class名, class名是由应用的“前缀配置”和widget的名字(NAME属性)组成。比如:
// A method on a Spinner widget instance,
// with Spinner.NAME = "spinner";
renderSpinnerButton: function() {
// generates the class name "yui3-spinner-increment-major"
var btnClassName = this.getClassName("increment", "major");
}
注意:当需要在一个静态的执行环境下创建class名时,ClassNameManager.getClassName(arg1, arg2, arg3 …)方法可以代替上述方法,应用的NAME属性通过第一个参数传入。比如:
// Generates the class name "yui3-spinner-button"
Spinner.BUTTON_TEMPLATE = Y.ClassNameManager.getClassName(Spinner.NAME, "button");
这个静态方法(是Widget类的静态方法)可以用来生成前缀为“yui3-widget”的class名。这对于插件来说相当有用,因为不管该插件被-插在哪个实例上,它都需要一个固定的class名。如:
// 生成class名"yui3-widget-shim"
Widget.getClassName("shim");
最佳实践是,避免用像”visible”, “disabled”, “focused”这样的style属性控制widget的状态。而是使用class名来反映widget的状态:
Attribute/State | CSS Class Applied |
---|---|
visible | yui3-[widgetname]-hidden |
disabled | yui3-[widgetname]-disabled |
focused | yui3-[widgetname]-focused |
在上述的定义中,“[widgetname]”代表widget的名字(比如:“menu”、“overlay”、“slider”)。状态标识符与widget名组合,让每个widget都能自定义操作显示状态的方式。而且,在IE6下也能正常工作。
/* Hide by positioning off-screen */
.yui3-menu-hidden {
top:-10000em;
left:-10000em;
}
/* Hide by flipping visibility */
.yui3-overlay-hidden {
visibility:hidden;
}
/* Hide by flipping display */
.yui3-slider-hidden {
display:none;
}
然而,如果想使用IE6也兼容的格式,就需要为每个widget的每个状态定义相应的css名。在以上基于widget状态的CSS规则中,每个widget都需要定义“yui3-[widgetname]-hidden”规则来实现对可见性的控制。是否要提供另两种对状态的支持,取决于这个widget是否需要为“disabled”和“focus”两种状态提供特殊的UI控制。
Widget类为每一个DOM事件(click,mouseover)发布和触发自定义事件。这些事件在boundingBox内被触发。和Widget类的其他自定义事件一样,这些事件以widget名字作为前缀(“menuItem:click”),事件监听器的默认上下文对象是触发事件的widget,而不是触发该DOM事件的节点。这些事件使得监听UI事件成为widget API的一部分,而不需要考虑构成widgetUI的DOM元素。
因为很多Widget实例都会发布和触发这些事件,Widget类默认做以下事情,以保证这些事件的触发机制在不同的widget实现中都是一致的。
要开发自己的widget,需要创建一个扩展Widget类的类,然后实现下图显示的属性和方法。
先定义构成widget的属性(ATTRS静态属性)。这些属性定义widget公开的状态和API。然后根据不同职责实现initializer、destructor、renderUI、bindUI、syncUI方法,以及属性状态修改的处理函数和API。
Extending The Widget Class这个例子教你使用widget结构模板一步一步实现一个Spinner widget。该例子和模板为开发者提供了一个很好的开始。
另外,widget结构模板可以从此结构模板文件获得,可以在该文件的基础上开发widget。
除了可以基于任意Widget类创建子类,实现自定义widget外,YUI3还提供两个代码重用机制。这两个机制可以用来打包可重用的widget功能的代码,不需要将这些功能绑定到一个静态的类层级中。这两个代码重用机制就是插件(plugins)和扩展(extensions),可以从Base类获得这两种机制的支持。
一般来说,开发人员可以通过创建Widget类的子类或者创建用Y.extend方法获取Widget类功能的类的子类来实践widget的功能和特性。
这些特性和功能应被打包成扩展或者插件,以便在多个类(extension情况下)或多个实例(plugin情况下)中都能通用。
我们总是会遇到关于功能和特性是以插件的形式存在还是以扩展的形式存在的问题。widget开发者需要根据widget的用例来考虑widget的设计。
正如上述所说,插件和扩展提供一个创建小模块功能的机制,这些功能可以添加到widget的核心实现中。他们的差异如下:
当你开始用YUI3开发widget时,有些打包好的扩展可以用于向你的自定义widget类添加功能。使用Base.build (或者 Base.create, Base.mix)方法。
扩展 | 功能 |
---|---|
widget-position | 向类添加XY定位支持 |
widget-position-align | 向类添加XY对齐支持 |
widget-position-constrain | 向类添加有约束的XY定位支持 |
widget-stack | 向类添加层级(zIndex)支持 |
widget-stdmod | 向类添加标准模块(header, body, footer)支持 |
widget-parent | 添加让widget包含、管理、选择子widget的支持 |
widget-child | 添加让widget可以被包含在一个父widget中的支持 |
widget-parent和widget-child插件提供的功能,让开发者能创建嵌套的widget,该功能值得详细介绍一下:
添加让widget包含、管理、选择子widget的支持:
你还可以看看这些使用多个扩展创建的复杂widgets:
“MyExtension” 模板文件提供了创建扩展的模板。 widget 插件 YUI3库在发布时,带了几个widget插件,还有一些例子教你如何创建自己的插件:
另外,YUI Gallery中也有不少插件的例子。如Modal Overlay Plugin和Widget IO Plugin。 “MyPlugin” 模板文件提供了创建自己插件的模板。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/158023.html原文链接:https://javaforall.cn