Dijit 是一个强大的框架,我们可以用来创建简介,专业的界面。有时,那意味着我们需要一个带有选项的菜单,带来我们像桌面应用一般的体验。有了wijit/Menu, 我们有了一个易于使用的强大的工具区创建这些界面。
难度:初学者
dojo 版本:1.8
作者:Dylan Schiemann
译者:Leslie ([email protected])
带有选项的菜单在UI领域是很熟悉的概念。不同的菜单有不同的形状大小,从水平菜单栏到下拉菜单,再到上下文或者右键菜单进行上下文相关操作。HTML提供的原生控件功能有限,并且简单的HTML/CSS 组合略显尴尬并且受制于功能和可用性。dijit/Menu*系列控件解决了这些问题。
dijit/Menu documentation 概括了菜单的种类和使用它们的方法。这个文档和API documentation 都是学习的好地方。我们将会参照这些资源并展开以此为一个简单的任务管理应用开发一套菜单。
我们将从简单的dijit/Menu——一个垂直的管理菜单项的控件。就像所有Dijit控件那样,dijit/Menu能够以声明风格来使用,我们在标签中配置我们的控件,又或者用编程的方式
用Javascript代码显式地配置和创建实例。为了演示,我们以此用每种风格创建最简单的菜单,这些菜单以此包括edit,view和任务选项。
这些代码实例将假设你已经创建了一个页面用来载入Dojo toolkit和Claro主题。你可以访问previoustutorials回顾之前的知识。
<body class="claro"> <script> // Require the Menu and MenuItem class, and the dojo/parser, // and make sure the DOM is ready require([ "dijit/Menu", "dijit/MenuItem", "dojo/parser", "dojo/ready" ], function(Menu, MenuItem, parser, ready){ ready(function(){ parser.parse(); }); }); </script> <div id="mainMenu" data-dojo-type="dijit/Menu"> <div id="edit" data-dojo-type="dijit/MenuItem">Edit</div> <div id="view" data-dojo-type="dijit/MenuItem">View</div> <div id="task" data-dojo-type="dijit/MenuItem">Task</div> </div> </body>
View Demo
<body class="claro"> <div id="mainMenu"></div> <script> // Require the Menu and MenuItem class // Make sure the DOM is ready require([ "dijit/Menu", "dijit/MenuItem", "dojo/ready" ], function(Menu, MenuItem, ready){ ready(function(){ // create the Menu container var menu = new Menu({ }, "mainMenu"); // create and add child item widgets // for each of "edit", "view", "task" menu.addChild( new MenuItem({ id: "edit", label: "Edit" }) ); menu.addChild( new MenuItem({ id: "view", label: "View" }) ); menu.addChild( new MenuItem({ id: "task", label: "Task" }) ); menu.startup(); }); }); </script> </body>
View Demo
当使用dijit/Menu时,一个菜单式有一系列的菜单项组成。每个菜单都是一个控件实例,并且每个菜单项都是它自己的控件实例。菜单包含菜单项,并且独立的菜单项能够与一个子菜单相关联。在上面的例子中,我们使用了一般的dijit/MenuItem控件类去表示在菜单中的菜单项。尽管如此,一系列的菜单项能够包含分隔符和包含子菜单的菜单项。这些特殊类型的菜单项中的每一种都有自己专属的控件类型(Widget Class)。
dijit/MenuItem
在一个垂直菜单中的菜单项。支持可选图标(通过CSS),并且可选的快捷方式(快捷键)组合。 dijit/MenuItem和继承与此的控件能够通过键盘被点击和选中,并且支持全键盘导航。菜单项通过设置disabled标志位来设置视觉和功能上的disabled.
dijit/MenuBarItem
基于dijit/MenuItem的一个扩展,被用来表示水平菜单,包含有视觉和可用性的改变。
dijit/PopupMenuItem
基于dijit/MenuItem的一个扩展,表示拥有子菜单的一个菜单项。子菜单能够通过点击或者在菜单项上停顿,亦或者通过带有快捷键的键盘访问。
dijit/PopupMenuBarItem
一个水平方向版本的dijit/PopupMenuItem
dijit/MenuSeparator
一个非交互式的菜单项间的分隔器
将我们如何想象我们的菜单结构与dijit/Menu的内部结构相互关联是非常有用的。最终,子菜单也仅仅是dijit/PopupMenuItem的弹出属性。当编程式地创建一个菜单,我们将一个菜单实例非配给一个菜单项的popup属性以便层级地创建菜单。当申明式地创建一个菜单时,可以通过元素内嵌来表示菜单的层级关系。为了创建一个有子菜单的菜单,我们可以简单地在一个菜单项中内嵌一个菜单;第一个元素(通常是一个</span>)被用来表示菜单项的标注。
声明风格
<body class="claro"> <script> // Require dependencies require([ "dijit/Menu", "dijit/MenuItem", "dijit/PopupMenuItem", "dojo/parser", "dojo/domReady!" ], function(Menu, MenuItem, PopupMenuItem, parser){ parser.parse(); }); </script> <div id="mainMenu" data-dojo-type="dijit.Menu"> <div id="edit" data-dojo-type="dijit.MenuItem">Edit</div> <div id="view" data-dojo-type="dijit.MenuItem">View</div> <div id="task" data-dojo-type="dijit.PopupMenuItem"> <span>Task</span> <div id="taskMenu" data-dojo-type="dijit.Menu"> <div id="complete" data-dojo-type="dijit.MenuItem"> Mark as Complete </div> <div id="cancel" data-dojo-type="dijit.MenuItem"> Cancel </div> <div id="begin" data-dojo-type="dijit.MenuItem"> Begin </div> </div> </div><!-- end of sub-menu --> </div> </body>View Demo
编程风格
<body class="claro"> <div id="mainMenu"></div> <script> // Require dependencies require([ "dijit/Menu", "dijit/MenuItem", "dijit/PopupMenuItem", "dojo/domReady!" ], function(Menu, MenuItem, PopupMenuItem){ // create the Menu container var mainMenu = new Menu({ }, "mainMenu"); // create Menu container and child MenuItems // for our sub-menu (no srcNodeRef; we will // add it under a PopupMenuItem) var taskMenu = new Menu({ id: "taskMenu" }); // define the task sub-menu items taskMenu.addChild( new MenuItem({ id: "complete", label: "Mark as Complete" }) ); taskMenu.addChild( new MenuItem({ id: "cancel", label: "Cancel" }) ); taskMenu.addChild( new MenuItem({ id: "begin", label: "Begin" }) ); // create and add main menu items mainMenu.addChild( new MenuItem({ id: "edit", label: "Edit" }) ); mainMenu.addChild( new MenuItem({ id: "view", label: "View" }) ); // make task menu item open the sub-menu we defined above mainMenu.addChild( new PopupMenuItem({ id: "task", label: "Task", popup: taskMenu }) ); mainMenu.startup(); taskMenu.startup(); }); </script> </body>
View Demo
可以注意到在编程风格的例子中,唯一的一个“魔法”是将菜单项中的一个元素解析为菜单项的label。然后内嵌的菜单会被自动的认为是菜单项的弹出菜单。
我们已经提到一个子菜单仅仅是菜单项的一个popup属性。这意味这动态改变菜单之间的关系,并且多个菜单项引用同一个菜单实例作为他们的子菜单是可能的。根据上面的例子,我们可以在一个完全不同的UI的部分额外地将taskMenu分配为另一个dijit/PopupMenuItem的弹出菜单。
以上例子中并未提到,但使用编程方法完全可行的一个任务是在运行时重新分配子菜单。要完成这个任务,你可以简单地在dijit/PopupMenuItem上重新设置popup属性
以便指向一个新的子菜单。此外,你可以删除/摧毁原先的PopupMenuItem(如果不再使用,你也可以删除子菜单)并且插入一个新的合适的popup值。
所有的菜单系列共享相同的由Dijit提供的相同的可访问性。这意味这正确的ARIA角色被用来提供辅助技术来告知用户他们正在与何种控制相互,进而告诉他们如何使用。键盘可访问性被用来帮助低视力的,没有鼠标的用户。Tab键在菜单键移动焦点,并且方向键被用来在菜单和子菜单间移动。
就像dijit/form/Button和他的子类,dijit/MenuItem的实例拥有一个iconClass属性。这能够被用来增加一个特殊的CSS属性到一个菜单项的模板,以便在菜单中显示图标。以下是一个简单的,关于如何实现图标的例子,同时提供声明风格和编程风格示例。
<script> // Require dependencies require([ "dijit/MenuItem", "dojo/parser", "dojo/domReady!" ], function(MenuItem, parser){ parser.parse(); }); </script> <div id="task" data-dojo-type="dijit.MenuItem" data-dojo-props="iconClass: 'taskIcon'"> Task </div>
// Require dependencies require([ "dijit/MenuItem", "dojo/domReady!" ], function(MenuItem){ new MenuItem({ id: "task", label: "Task", iconClass: "taskIcon" }); });
iconClass属性指向一个CSS类,我们可以在我们的stylesheet中定义此类——例如:
<style> .taskIcon { background: url("./icons/task.png"); width: 24px; height: 24px; } </style>
至今,我们所使用的djijit/Menu被称为“导航菜单”——也就是说,静态地放置在页面上。我们可以非常简单地将这种菜单转变为上下文弹出菜单。
require([ "dijit/Menu", "dojo/domReady!" ], function(Menu){ var mainMenu = new Menu({ id: "mainMenu", contextMenuForWindow: true }); });
这给我们提供了一个上下文菜单。我们可以通过传入DOM nodes数组或者传入我们创建菜单时设置的targetNodeIds属性得到的IDs为某些元素配置上下菜单。
require([ "dijit/Menu", "dojo/domReady!" ], function(Menu){ var taskMenu = new Menu({ id: "taskMenu", targetNodeIds: ["task_0", "task_1", "task_2"] }); });
如果你需要在菜单创建后更新菜单与元素之间的绑定关系,可以在menu widget上使用bindDomNode和unBindDomNode方法
我们的下一个demo提供了一个全局的上下文菜单和另一个专属与某一个task items的上下文菜单。
View Demo
在demo中,你可以注意到在全局上下文中也有一个“Task”弹出菜单(重用了task items的上下文菜单)。尽管如此,直到你点击了某一个task item,不然没有“当前”item task——因为这个原因,我们刚开始时是这个菜单项无效,然后当用户选择了item时使之有效。
require(["dijit/registry"], function(registry){ registry.byId("task").set("disabled", false); }
<body class="claro"> <script> // Require dependencies require([ "dijit/Menu", "dijit/MenuItem", "dijit/MenuBar", "dijit/MenuBarItem", "dijit/PopupMenuBarItem", "dojo/parser", "dojo/domReady!" ], function(Menu, MenuItem, MenuBar, MenuBarItem, PopupMenuBarItem, parser){ parser.parse; }); </script> <div id="mainMenu" data-dojo-type="dijit.MenuBar"> <div id="edit" data-dojo-type="dijit.MenuBarItem">Edit</div> <div id="view" data-dojo-type="dijit.MenuBarItem">View</div> <div id="task" data-dojo-type="dijit.PopupMenuBarItem"> <span>Task</span> <div id="taskMenu" data-dojo-type="dijit.Menu"> <div id="complete" data-dojo-type="dijit.MenuItem"> Mark as Complete </div> <div id="cancel" data-dojo-type="dijit.MenuItem"> Cancel </div> <div id="begin" data-dojo-type="dijit.MenuItem"> Begin </div> </div> </div><!-- end of sub-menu --> </div> </body>View Demo
<body class="claro"> <div id="mainMenu"></div> <script> // Require dependencies require([ "dijit/Menu", "dijit/MenuItem", "dijit/MenuBar", "dijit/MenuBarItem" "dijit/PopupMenuBarItem", "dojo/domReady!" ], function(Menu, MenuItem, MenuBar, MenuBarItem, PopupMenuBarItem){ // create the Menu container var mainMenu = new MenuBar({ }, "mainMenu"); // create Menu container and child MenuItems // for our sub-menu (no srcNodeRef; we will //add it under a PopupMenuItem) var taskMenu = new Menu({ id: "taskMenu" }); // define the task sub-menu items taskMenu.addChild( new MenuItem({ id: "complete", label: "Mark as Complete" }) ); taskMenu.addChild( new MenuItem({ id: "cancel", label: "Cancel" }) ); taskMenu.addChild( new MenuItem({ id: "begin", label: "Begin" }) ); // create and add main menu items mainMenu.addChild( new MenuBarItem({ id: "edit", label: "Edit" }) ); mainMenu.addChild( new MenuBarItem({ id: "view", label: "View" }) ); // make task menu item open the sub-menu we defined above mainMenu.addChild( new PopupMenuBarItem({ id: "task", label: "Task", popup: taskMenu }) ); mainMenu.startup(); taskMenu.startup(); }); </script> </body>
在采用菜单的Dijit中有一系列的混合控件。这些包括dijit/form/ComboButton和dijit/form/DropDownButton。这些原则与我们如何创建内嵌菜单非常相似。例如,我们可以按照如下的方式创建一个ComboButton:
声明风格<body class="claro"> <script> // Require dependencies require([ "dijit/Menu", "dijit/MenuItem", "dijit/form/ComboButton", "dojo/parser", "dojo/domReady!" ], function(Menu, MenuItem, ComboButton, parser){ parser.parse; }); </script> <div id="comboButton" data-dojo-type="dijit.form.ComboButton"> <span>Do Something</span> <div data-dojo-type="dijit.Menu"> <div data-dojo-type="dijit.MenuItem">Edit</div> <div data-dojo-type="dijit.MenuItem">View</div> <div data-dojo-type="dijit.MenuItem">Task</div> </div> </div> </body>
<body class="claro"> <div id="comboBtn"></div> <script> // Require dependencies require([ "dijit/Menu", "dijit/MenuItem", "dijit/form/ComboButton", "dojo/domReady!" ], function(Menu, MenuItem, ComboButton) { var menu = new Menu({ id: "mainMenu" }); menu.addChild( new MenuItem({ label: "Edit" }) ); menu.addChild( new MenuItem({ label: "View" }) ); menu.addChild( new MenuItem({ label: "Task" }) ); // create a ComboButton and add the Menu var comboBtn = new ComboButton({ label: "Do Something", dropDown: menu }, "comboBtn"); menu.startup(); comboBtn.startup(); }); </script> </body>View Demo
我们有数不胜数的方式去使用菜单,并且每一种使用方式都有自己的使用场景。Dijit给你提供了一种健壮的菜单类和一系列常用的UI模式的实现。你可以与菜单交互并且配置与改变菜单项的外观。这里有许多其他事件我们没有在这篇教程中涵盖,但是你可以自己去熟悉:
onItemHover
当用户的指针移过菜单项时被调用。
onItemUnHover
当用户的指针离开MenuItem是被调用。
onItemClick
当菜单项被点击时被调用。这本质上是连接到每一个菜单项的onClick的一个可选方法。
onOpen
当菜单显示或者被打开时被调用。
onClose
当菜单被隐藏或者被关时被调用。
查看API文档以获取过多关于菜单控件的细节。就像任何其他Dijit widget, 当你的需求无法被满足是,dijit/Menu和其他关联的类都是可以可扩展的。
Dijit中的菜单使我们能够方便的创建菜单栏和子菜单并与之交互。我们已经看到了如何通过声明风格和编程风格创建各种菜单。这仅仅是Dijit武器库中的一小部分,Dijit致力于创建更加杰出的用户体验。