简介
通过使用 DHTML effects , 有选择地显示或隐藏 HTML 页面的一部分 , 往往可以用来创建在 HTML 中动态显示的菜单。 WebLogic Portal 创建菜单使用的技巧是,将菜单项包围在标准的 HTML DIV 元素中。这些元素是通过嵌入在页面,以及JavaScript 支持 文件(如 menu.js )中的 JavaScript 代码,来打开和关闭的。下面是显示方式的简单示例:
<DIV id=‘menu1Title‘ onClick=‘toggle("menu1")‘>File</DIV><DIV id=‘menu1‘> <DIV id=‘menuItem1‘>Open</DIV> <DIV id=‘menuItem2‘>Close</DIV> <DIV id=‘menuItem3‘>Exit</DIV></DIV>
上面是一个很标准的例子 , 一般来说 , 门户开发人员没有必要知道这一底层实现机制。但是,有些浏览器极不稳定,从而导致上述技巧出现问题。在 Mozilla 和 Firefox 中运行正常的解决方案,在 IE 中使用时却会产生问题 , 原因在于处理窗口控件的 IE 工件。
窗口控件 是 Windows 开发人员使用的一个术语 , 指的是具有窗口句柄的控件 ( HWND ,为您这样的 Win32 高手提供 ) 。这些控件由操作系统来管理和实施 , 而不是浏览器。 Microsoft 创建 IE 时,选择使用了现有的组合框(窗口控件)的 Windows 实现工具作为 HTML SELECT元素的实现工具。此外 , 其他嵌入式对象 ( 包括 ActiveX 、 Flash 和 Adobe PDF 查看器 ) 也都是作为窗口控件来现实的。
本文的目的在于,您不必了解句柄和 Win32 ,但是需要认识到,一旦操作系统(而非浏览器)管理了窗口控件,基于窗口控件的 HTML 元素,就可能具有了与规则 HTML 元素不同的字符。
出现的问题
简单地说 , 在 Web 页面中动态放置和显示 DIV 元素时,窗口控件就成了 IE 中长期存在的问题根源。尤其是,窗口控件在通过 DIV 显示时,将破坏预期的效果。 这一点在使用 DIV 元素显示菜单的 Web 页中尤为明显,遗憾的是,这一问题严重地影响着 WebLogic Portal 中的菜单,如图 1 所示。
在此图中 , 第一个 portlet 中的各种框遮盖了下拉菜单 , 结果导致菜单无法有效使用。如大多数 HTML 开发人员所知道的那样,在浏览器中,分层的 HTML 元素的显示是由 zIndex 属性来控制的。具有较高 zIndex 的元素显示在具有较低 zIndex 的元素的上面。问题产生的原因是, IE 对 HTML 元素的 zIndex 和窗口控件的 zIndex 的处理方式不同,它总是将窗口控件置于所有 HTML 元素的上面。这可能导致无法将 DIV 显示在 SELECT 上面 , 而不是让 SELECT 穿过 DIV 显示。
此问题在 DHTML zIndex 属性的 MSDN documentation 中已有介绍 , 其中明确阐述了 zIndex 属性并不支持窗口控件。
解决方案
在 IE 5.5 及其更高版本中,解决此问题极为简单 , 因为它们具有相当新的 JavaScript 技巧,如对 IFRAME 元素行为的更改。在 IE 5.5 及其更高版本中 , IFRAME 元素的 zIndex 同时考虑到了窗口控件和 HTML 元素。这意味着,用户可以将 IFRAME 置于 SELECT之上,它将遮盖 SELECT 窗口控件。此外,还可以将 DIV 覆盖在 IFRAME的上面,它所遮盖的 IFRAME 或 SELECT 都将不再显示。
这一特殊的 IFRAME 被 置于 DIV 的 下面以隐藏窗口控件 ,它被 称为 垫片 , 因为它仅用于遮盖 DIV 。许多资料都讨论了如何使用此技巧,在下述参考部分中,我们重点介绍其中之一。除此之外,在 MSDN 文档资料中,还包括了对 IFRAME 元素的 zIndex 属性的行为更改。
在 WebLogic Portal 中使用垫片技术
现在我们了解了垫片技术 , 我们需要将其应用于 WebLogic Portal 菜单。幸运的是, WebLogic Portal 中的菜单是由 JavaScript 生成的,因此我们将垫片用于菜单极为方便。只需要修改一个文件(menu.js)即可,该文件位于 framework/skins/default/js 目录中。证明 menu.js 是如何显示菜单的,超出了本文讨论的范围,但是关于此过程的详细信息可以在 WebLogic Portal User Interface Framework Guide 中找到。
首先我们需要进行的修改是,添加所有新的垫片功能 , 以便根据需要创建、显示和隐藏这些垫片。注意,因为当用户通过各子菜单下溯时 WebLogic Portal 会 同时显示出很多菜单,所以我们需要具有创建和显示多个垫片的能力。从本质上说,在垫片和每个菜单之间存在着一一对应的关系。创建每个垫片时 , 它都具有一个同与之关联的菜单相对应的标识。
不幸的是 , 因为在 WebLogic 服务包之间 , menu.js 可能发生变更 ,所以 提供经过修改的 menu.js 是不可行的。反之,要应用此解决方案,用户需要手动修改 menu.js 文件,但是请放心,这是一个很简单的任务。
第一个步骤是将那些基本的 JavaScript 功能,添加到以后打开和关闭菜单时将要引用的文件中。这些功能如下所示,将这些功能附加在 menu.js 的结尾处即可。
//Opens a shim, if no shim exists for the menu, one is createdfunction openShim(menu,menuItem){ if (menu==null) return; var shim = getShim(menu); if (shim==null) shim = createMenuShim(menu,getShimId(menu)); //Change menu zIndex so shim can work with it menu.style.zIndex = 100; var width = (menu.offsetWidth == 0 ? menuItem.renderedWidth : menu.offsetWidth); var height; if (menu.offsetHeight == 0) { var menus = getMenuItemCount(menu); height = menuItem.renderedHeight * menus; } else { var height = menu.offsetHeight; } shim.style.width = width; shim.style.height = height; shim.style.top = menu.style.top; shim.style.left = menu.style.left; shim.style.zIndex = menu.style.zIndex - 1; shim.style.position = "absolute"; shim.style.display = "block";}//Closes the shim associated with the menufunction closeShim(menu){ if (menu==null) return; var shim = getShim(menu); if (shim!=null) shim.style.display = "none";}//Creates a new shim for the menufunction createMenuShim(menu){ if (menu==null) return null; var shim = document.createElement("<iframe scrolling=‘no‘ frameborder=‘0‘"+ "style=‘position:absolute; top:0px;"+ "left:0px; display:none‘></iframe>"); shim.name = getShimId(menu); shim.id = getShimId(menu); //Unremark this line if you need your menus to be transparent for some reason //shim.style.filter="progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"; if (menu.offsetParent==null || menu.offsetParent.id=="") { window.document.body.appendChild(shim); } else { menu.offsetParent.appendChild(shim); } return shim;}//Creates an id for the shim based on the menu idfunction getShimId(menu){ if (menu.id==null) return "__shim"; return "__shim"+menu.id;}//Returns the shim for a specific menufunction getShim(menu){ return document.getElementById(getShimId(menu));}function getMenuItemCount(menu){ var count = 0; var child = menu.firstChild; while (child) { if (child.nodeName=="DIV") count = count + 1; child = child.nextSibling; } return count; }
将这些新功能粘贴到 menu.js 的底部后 , 下一个步骤是将这些新功能应用于各种菜单功能中。第一个要更改的功能称为openMenu()。如果在menu.js中搜索它,则可以看到它带有三个参数:menuItem、menu和depth。在此,您需要做的只是在该方法的结尾处添加一个新的代码行:
openShim(menu,menuItem);
注意 , 此为对我们先前添加的一种功能的调用。它的作用是,确保每次打开菜单时创建和显示垫片。
下一个修改是 , 确保在关闭菜单时关闭垫片。为此,需要修改menu.js中的closeAllChildren()方法。尤其是,需要在该方法中添加一个行;首先在该方法中查找此现有行:
subMenu.style.display = "none";
在此行的后面 , 添加一个关闭垫片的新行 :
closeShim(subMenu);
注意 , 这一行也是对我们先前粘贴的一种功能的调用。
恭喜 ! 您已经完成的修改。如果没有什么差错 , 则在 IE 5.5 和更高版本中 , 您的 WebLogic Portal 菜单现在应该正常运行了 , 如图 2 所示。
结束语
本文阐明了通过 Internet Explorer 呈现页面时 HTML 元素有时被其他 DHTML 元素隐藏的问题。我们介绍了使用IFRAME垫片方便地解决此问题的方式,并演示了如何修改 WebLogic Portal 以利用此解决方案的方法。结果形成了提高门户外观并增强用户满意程度的解决方案。
参考资料
关于作者
Gerald Nunn 是 BEA 系统专业服务的业务主任顾问。
联系客服