介绍 Wdiget 之前,我想说说一个关于摘取网页的小插曲。之前我为大家介绍过摘取网页的好工具,ScrapBook 。ScrapBook 在处理纯视觉的页面,也就是 CSS/HTML 的时候,表现较为满意,只不过在选择下载“JavaScript”,就有那么一点力不从心了,——这不,人家也在上面说了“JavaScript may cause some problems”。的确,我使用 ScrapBook 的时候真是会碰到一些页面渲染不正确的问题。那,有没有比较好的工具,说白了,就是摘取页面的“银弹”。呵呵,该是祭出 WinHTTrack 的时候。
怎么说 WinHTTrack 强大呢?比如说这个例子,我们当前要对付的是 Ext JS Core Manual页面 ,普通方法(包括 ScrapBook)摘取回来之后,不能加载其中的正文。估计原因是 ScrapBook 不能分析 XHR 的缘故,就无法获取内容正文。换 WinHTTrack 试试?嗯,摘取回来了,但直接打开还是不行,部署在 IIS 中,OK 啦,跟浏览 extjs.com 的一模一样!
这就是该工具的强大之处,离线浏览器的功用就应该在于此。不过单单为下载一张页面而使用它好像有点大材小用,因为 WinHTTrack 本身可以下载整个网站的。可是话又说回来,怎么在 WinHTTrack 中只选择某一页面下载,不下载其它连接, 这一点小弟我还不知道,希望熟悉的同志相告。
值得一提的是,WinHTTrack 是开源项目,主页在:http://www.httrack.com/ 。
言归正传,我们说说这个刚新鲜拿回来的 Manual Widget(点击以下图片可以看到该 Wdiget 演示):
Manual 效果如上图。接着看看这个 Manual 的内部原理。打开源码 overview.js,看到作者是 Tommy,一个不知名的官方人员。乍看一下,Ext.app.Overview 不算复杂,我们转向 HTML 去了解结构,回过头来再看 JS。Manual Widget 当前支持两级的菜单。列表元素 ul#manual-nav 为左边的导航菜单栏目,子元素 li.group group-expanded group-active是第一级的菜单,该菜单的“<a class="group-expand-btn">Expand Group</a>”为固定的元素,每一个菜单中都会有的,表征为菜单的“按钮”。而“<a class="group-title" href="content/overview.html">Overview of Ext Core</a>”就是菜单的标题;同级的 ul#manual-nav 再嵌套一个列表元素 ul#group-sections,存放二级菜单。二级菜单中的 A 连接指向的“content/overview.html#download”便是内容正文,#XXX 表示指定的区域是哪一块。
对 Widget 结构比较熟悉后,我们进入 JS 源码。打开 js/overview.js,发现启动该程序的是 Ext.onReady(Ext.app.Overview.init, Ext.app.Overview);。作者把该 Widget 写成单例的模式了。init() 函数如下:
init : function() { // Initializing the application menu = Ext.get('manual-nav'); content = Ext.get('content'); wrap = Ext.get('content-wrap'); this.initMarkup(); this.initEvents(); this.syncHeight(); // by default navigate to the overview this.navigateTo(menu.child('li.group', true)); },
Widget 与 JS 脚本之间透过 id 或 class 属性操作控制。和函数其他的地方一样,id、class selector 等的 HTML 是写死的,作者并没有进一步抽象出来。故此,我们变动了HTML页面 id 或 class 的话,就在 widge t对象本身的源码上修改。
建议 Manual Widget 可作说明书、简报等的 UI 用途。
如果说 Manual Wdiget 的菜单是垂直布局的,那么接着下来介绍的 Carousel Widget 便是水平布局的。—但是请打住,尽管两者的“外观”上可以作这样一种转换(shift),但真正来说,下面的 Widget 其实是扩展 Carousel 的应用。如下Widget首次出现在介绍 Ext Core 的博文 中。点击以下图片可以看到该 Wdiget 演示。
至于 Carousel 是什么词儿?根据 Google 大神给出的翻译如下:1 . 旋转木马 2 . 一连串的繁忙活动;走马灯似的更迭。相比之下,我宁愿更喜欢用“画册 Gallery”去表示该意涵,不知是否尚有商榷之处?因为实际上,对于怎么浏览一批对象,把对象陈列出来,运行这样的 UI 就是 Carousel 了。一般而言,Carousel 应具备自动播(auto-play)、动画过渡(animations)、自定义导航内容(custom navigation content)等等的功能。而我们现在看到的这个例子是扩展 Carousel 后一个特例。实际上它分为上下两部分,上面的解释说明和下面的图片导航。上面的就是原本 Carousel Widge t部分,如下对应的 HTML:
<div id="examples-carousel"> <div class="carousel-item"> <a href="http://extjs.com/products/extcore/manual/" mce_href="http://extjs.com/products/extcore/manual/" target="_blank" title="Click to go to the manual"> <img src="Ext%20JS%20-%20Blog_files/manual-icon-normal.png" mce_src="Ext%20JS%20-%20Blog_files/manual-icon-normal.png"></a> <h4> <a href="http://extjs.com/products/extcore/manual/" mce_href="http://extjs.com/products/extcore/manual/" target="_blank" title="Click to go to the manual"> Manual</a></h4> <p> Wether you are new to or an experienced user of Ext, the manual is an extensive resource in which everyone will be able to find and learn something new. The manual itself is a great example of how you can leverage Ext Core to spice up your website with a minimal amount of elegant and readable code. <a href="http://extjs.com/products/extcore/manual/" mce_href="http://extjs.com/products/extcore/manual/" class="carousel-view-example" target="_blank" title="Click to go to the manual"> Read Manual</a> </p> </div> …… </div>
当然余下的 div.carousel-item 还有更多。这些 div.carousel-item 便是候选的内容,每一时刻只出现一项,即 div.carousel-item里面的内容。怎么触发内容显示呢?就是下半部分的 Widget 通过事件触发。下半部分算是扩展 Carousel Widget 的地方了。HTML 结构如下:
<div id="carousel-navigation"> <img id="carousel-navigation-arrow" src="Ext%20JS%20-%20Blog_files/nav-arrow.gif" mce_src="Ext%20JS%20-%20Blog_files/nav-arrow.gif"> <div id="carousel-navigation-shortcuts"> <img src="Ext%20JS%20-%20Blog_files/manual-icon-small.png" mce_src="Ext%20JS%20-%20Blog_files/manual-icon-small.png" class="active"> <img src="Ext%20JS%20-%20Blog_files/carousel-icon-small.png" mce_src="Ext%20JS%20-%20Blog_files/carousel-icon-small.png"> <img src="Ext%20JS%20-%20Blog_files/lightbox-icon-small.png" mce_src="Ext%20JS%20-%20Blog_files/lightbox-icon-small.png"> <img src="Ext%20JS%20-%20Blog_files/menu-icon-small.png" mce_src="Ext%20JS%20-%20Blog_files/menu-icon-small.png"> <img src="Ext%20JS%20-%20Blog_files/jsonp-icon-small.png" mce_src="Ext%20JS%20-%20Blog_files/jsonp-icon-small.png"> <img src="Ext%20JS%20-%20Blog_files/tabs-icon-small.png" mce_src="Ext%20JS%20-%20Blog_files/tabs-icon-small.png"> <img src="Ext%20JS%20-%20Blog_files/combi-icon-small.png" mce_src="Ext%20JS%20-%20Blog_files/combi-icon-small.png" class="last-item"> </div> </div>
该部分就是简单罗列图片。通过 div#carousel-navigation-shortcuts.click事件触发上半部分内容的变动,如下的定义:
var carousel = new Ext.ux.Carousel('examples-carousel', { itemSelector: 'div.carousel-item', interval: 5, autoPlay: true, transitionType: 'fade', transitionDuration: 0.3, hideNavigation: true }); var shortcuts = Ext.query('#carousel-navigation-shortcuts > img'); Ext.fly('carousel-navigation-shortcuts').on('click', function() { var index = shortcuts.indexOf(this.dom); carousel.pause(); carousel.setSlide(index); }, null, { delegate: 'img' }); carousel.on("change", function(slide, index) { var shortcut = Ext.get(shortcuts[index]); var arrow = Ext.get('carousel-navigation-arrow'); shortcut.radioClass('active'); var left = shortcut.getOffsetsTo('carousel-navigation')[0] + (shortcut.getWidth() / 2) - (arrow.getWidth() / 2); arrow.setLeft(left + 'px'); });
其中,登记该单击事件的时候,一定要{ delegate: 'img' } 指定 img 元素才可以接受该事件。var shortcuts = Ext.query('#carousel-navigation-shortcuts > img'); 是查询元素集合方法,返回 HTMLElement [] 的类型的数组,供事件处理函数中的 var index = shortcuts.indexOf(this.dom); 中的匹配使用。找到用户选定的 index 之后,就执行 carousel.setSlide(index); 指定哪一项内容显示。
接下的 carousel.on("change",……事件就是控制“箭头”左右移动的事件。另外,Ext.ux.Carousel 类还有 'beforeprev','prev','beforenext','next','play','pause','freeze','unfreeze' 这些事件,允许用户进一步扩展。
以上JS就是创建新 Ext.ux.Carousel 实例的过程,作者写在 HTML 文档中。应该说这是比较高阶的应用,那么追根朔源,能不能有比较简单的 Carousel 的应用呢?不错,Ext JS Core 早就为我们准备好了。Ext.ux.Carousel,标准 Ext.util.Observable的子类,我们还原到普通的应用。如下的 Demo,点击图片直接进入演示 :
例子中有三个不同的 carousel 应用,第一个是简简单单的带导航栏的画册,实现语句:new Ext.ux.Carousel('simple-example');这一行。simple-example 是图片的 div 容器,图片都是预加载的,因为图片数量较多的话,应该考虑扩展或 override Carousel 类,修改预先全部加载的模式。第二个例子消失了导航栏,是自动播放的;第三个例子就是连续不间断的过渡,适合于放置文本。如下是配置的详细情形:
new Ext.ux.Carousel('simple-example'); new Ext.ux.Carousel('full-example', { itemSelector: 'img', interval: 3, autoPlay: true, showPlayButton: true, pauseOnNavigate: true, freezeOnHover: true, transitionType: 'fade', navigationOnHover: true }); new Ext.ux.Carousel('html-example', { itemSelector: 'div.item', interval: 5, autoPlay: true, transitionEasing: 'easeIn' });
比较起 Carousel 第一个例子,无论使用方式还是出来的效果都很不同,当然是“画册”的应用比较简单、直观。同一个基类,做出来的 UI Widget 可以如此别致不同,不得不强调一下人家复用代码之奇……值得效法。
(待续下篇……)
本文所说例子的打包文件,可以从这里点击下载 ,大小 989KB。