js下拉菜单实现与可访问性问题的一些思考

这篇文章发布于 2010年09月10日,星期五,00:06,归类于 JS实例。 阅读 135093 次, 今日 6 次 11 条评论

 

一、俗耐的开篇语

关于下拉菜单的可用性问题,我之前一直都是忽略的,可以说是不知道,常常仅仅止步于眼前的效果上。前段时间看到了Roger的”Accessing Nav Drop-Downs“一文,就是讲了下下拉菜单的可用性问题。同时,巧的是,最近在看淘宝UED翻译的《ppk谈JavaScript》一书,其多次提到了可访问性的问题,尤其在p28~p37对JavaScript及其一些可用性问题发表了自己的看法。其中主要的观点和注意事项与Roger的文章是一致的。

这些阅读的经历让我意识到自己长时间忽略下拉菜单的可访问性,于是开始结合实际情况,思考自己以后需要注意和提高的地方。这让我对下拉菜单实现方式选择、标签的使用等有了更加明确的认识。不太成熟的思考,仅用于交流。再具体讲述下拉菜单的可访问性之前,先简单说说下拉菜单以及下拉菜单的实现吧。

二、关于下拉菜单及其实现

百度百科对“下拉菜单”一词的解释是:以条形菜单栏和菜单栏中每个菜单项的弹出菜单窗口两部分组成,一般作为应用系统的主菜单使用。不过这段话就像《盗梦空间》一样,让人很难懂。通俗点讲,就是“经过/会点击就会显示列表的菜单”就叫做下拉菜单。在web上非常之常见,例如~~我随便打开个页面,啊,就像是我浏览器现在显示的百科的页面的右上角:
百度百科右上角下拉菜单 张鑫旭-鑫空间-鑫生活

或是隔壁的微博页面,啊,果然,看左上角的广场下拉:
新浪微博左上角下拉 张鑫旭-鑫空间-鑫生活

恩恩,看来下拉菜单就像是男人一样,满地都是。就不一一举例了,关于下拉菜单的实现,那方法可就多了,class切换,属性绑定,js定位等,不同的页面,不同的设计,不同的架构,就有不同的实现方法。由于每个项目,每个页面的情况都不一样,所以,不能轻易的下结论,你是大熊猫,是国宝,它是小野猫,是杂草。但是,就可用性而言,不同的方法优劣还是有标准来评判的,这个在后面会自然而然的展示。

现在,举个切换class实现下拉效果的简单例子,实例菜单原型来自Mtime时光网//zxx:Mtime创始人在新浪微博上很活跃,你有兴趣可以follow他,@马日拉,你有没有觉得这个名字很有遐想的空间呢?,截图如下:
Mtime时光网导航下拉截图  张鑫旭-鑫空间-鑫生活

此导航下拉的每个下拉内容都已经通过CSS定位好了,但是,考虑到加载的原因,其下拉内容默认是未装载的。也就是说,是鼠标移至导航内容上,才动态load下拉div并嵌入导航li标签内的,如下图所示:
下拉div append 张鑫旭-鑫空间-鑫生活

当然,作为静态demo页面,没有必要动态load下拉内容,所以,demo页面的下拉div默认就是隐藏且装载好的,于是,我们就可以通过简单的class切换实现下拉效果。
首先是HTML结构,见下图:
时光网下拉菜单的HTML结构 张鑫旭-鑫空间-鑫生活

核心CSS代码如下:

.i_n_l{display:none;}
.showtime_hover .i_n_l,.quiz_hover .i_n_l,.home_hover .i_n_l,.movie_hover .i_n_l,.tv_hover .i_n_l,.person_hover .i_n_l,.blog_hover .i_n_l,.group_hover .i_n_l{display:block;}

可见,我们只要让li标签的class,例如“我的时光”所在li标签,由”home”变成”home_hover”就可以控制下拉菜单的显示与隐藏了,很简单吧,所以,相应的js代表就会类似于下面:

<script>
    var o = document.getElementById("navigationRegion").getElementsByTagName("li");
    var l = o.length;
    for(var i=0; i<l; i+=1){
        o[i].onmouseover = function(i){
            var cl = o[i].className;
            if(/_on$/.test(cl) || /_hover$/.test(cl)){
                  return;		  
            }
            o[i].className = cl + "_hover";  
        }(i);
        o[i].onmouseout = function(i){
            return function(){
                o[i].className = o[i].className.replace("_hover", "");    
            }
        }(i);
    }
</script>

然后,效果就如下图所示,截自IE6浏览器:
时光网下拉demo实现效果IE6下截图 张鑫旭-鑫空间-鑫生活

您可以狠狠地点击这里:时光网下拉菜单demo

恩,不错,效果良好,没有兼容性问题,js代码也算是简洁易懂,开起来似乎大功告成。要是以前的我,估计也就会到这里就结束了,没有bug,测试工程师不会来找茬了。但是,实际上,此处下拉的可访问性只能说是中等及格的水平。//zxx:不过貌似现在整个行业(即使淘宝这类重视前端的公司)的下拉菜单都只实现到这一程度,或许其中有着各种各样的原因,但我个人感觉还是整个行业的水平有待提高。

为何说看似不错的下拉菜单效果其实可用性低呢?继续下文。

三、下拉菜单可访问性问题

首先回答这个问题:什么是可访问性?
可访问性是指你的网页对任何人、在任何环境下都是可持续访问的。但是,在目前,要使得所有的网站在任何情况下都保持完美无缺的可访问性,这样的要求比要求老板升职还要高,是不实际的,但是,在有限的范围内,有着实际意义的,我们可以提高的地方还是很多的。

常见的可访问性问题有下面三个:
无脚本
这个主要是考虑到某些浏览器不支持JavaScript的情况。例如Google的页面中经常可见<noscript>标签,其就考虑到这一点。但是,就我个人的观点,如果您的网站不是面对亚非拉美这些国家,无脚本的问题其实是可以忽略的。当然,某些人员故意禁用JavaScript则另当别论。

没有鼠标
这是经常会遇到的。有些用户不使用鼠标,而是使用键盘,理由各种各样。例如我自己,有时候懒惰到境界的时候,要是左手已经搁在键盘上,就懒得抬起我的右手,去移动点击鼠标,多麻烦累人啊。直接左手指头垂直动个几厘米多轻松多方便啊;像我大学同学,电脑不知出了什么问题,鼠标都是使用不灵,所以他的大部分上网操作都是键盘完成的;还有些用户有手部残疾(或缺陷)而无法控制鼠标做出微笑的移动,按键为他们提供了一个很好的备选方案,除非JavaScript开发人员忘记照顾他们。而实际上,包括我在内的许多前端开发工程师,或是省功夫,或是其他什么原因,而将这部分人群当作成年的包裹,都扔掉了。

屏幕阅读器
有些人不能使用常规的浏览器。最典型的就是盲人和视力受损的人,他们无法看到屏幕上的任何东西。作为替代品,他们需要一个能把页面内容大声朗读出来的程序。这就是屏幕阅读器。

以前我纯粹按照自己臆想的经验,认为,盲人用户用电脑,估计就是个梦,就算有屏幕阅读器这类东西,估计也做不了什么操作。但是,路要自己走才知道多远,水要自己趟才知道多深。对自己没有经历过事情的认知往往总是不准确的,对于盲人用户与上网的认识也是如此。我发现自己认知的偏差是因为腾讯CDC的“闭上眼睛用QQ – 盲人用户探访实录”这篇文章。企鹅公司就是企鹅公司,有钱有人有访谈,这次访谈实录对于像我这样没有机会亲历盲人用户的开发人员来讲是非常宝贵的一手资料,让我意识到,盲人用户也有些他们精彩的世界,他跟我们这些视力良好的人一样,也是可以很好的体验到web给我们生活带来的快乐的。

//zxx:下面这段摘自“闭上眼睛用QQ – 盲人用户探访实录”一文。

盲人用户所有的电脑操作都依赖于读屏软件和键盘来完成。这三位用户现在使用的都是永德读屏软件,它安装后即在后台运行,把普通的操作系统变成了带语音的操作系统。盲人朋友依靠tab键,方向键和部分快捷键来调整目前光标的所在位置,而每按一次键盘,或屏幕上出现新的内容,或可操作界面上状态出现任何变化,系统都会有语音提示,这样盲人朋友也可以像普通人一样对电脑进行操作了。由于多年的训练,盲人所用的语音库的语速都非常快,普通人难以识别。顺带说一句,目前国内的读屏软件都不便宜,如永德是在1200左右,而一个盲人按摩师的月收入也就1、2千吧,问及他们是否会觉得这个软件太贵,老板娘笑了笑说:“我觉得值!”电脑和网络给盲人带来了全新的生活。
盲人用户使用电脑 张鑫旭-鑫空间-鑫生活
图片来自腾讯CDC 小黄和他的电脑(右边的数字键盘对他们很重要)

信息无障碍是我们应尽的公益责任,而我国是世界上盲人最多的国家,根据中国残联最近一次公布的数据(在2006年发布),视障患者的数量在1300万左右,若按照目前的速度增长下去,预计在2020年这一人数将达到5000万左右。要使盲人朋友能够更好的使用电脑,畅享网络生活,我们需要做的事情还有很多。

从上面三点可以看出,对键盘的支持是提高页面可访问性的最重要的因素。所以,我们再制作页面的时候,还需要多多考虑到键盘事件,尤其像上下左右键,TAB键和回车键。

四、国外支持键盘事件菜单

国外是有不少键盘支持下拉菜单的例子的,例如:
mozilla的官方首页
Mozilla官方首页下拉键盘操作 张鑫旭-鑫空间-鑫生活
当我们使用TAB键让导航项获取焦点后(IE和Firefox浏览器会有虚框),按下←键,或是→键,或是↓键,如果有导航项的话,就会显示下拉内容的。

又如yahoo开发示例的这个下拉菜单
yahoo开发下拉菜单截图 张鑫旭-鑫空间-鑫生活

与Mozilla首页下拉菜单相比,这里功能更多了些,支持二级菜单,由于导航非链接,所以还支持回车键显示下拉菜单。

上述两个实例都是一定程度上支持了键盘事件的下拉菜单的示例。但是,这里我要重重地转折!这里的键盘支持的实际应用价值是多少呢?开发的可行性又是多大呢?我的结论是:实际应用价值低,开发的可行性低。

首先是使用价值,通过移动方向键显示下拉菜单,就中国行情而言,就数十年来中国网民的使用习惯而言,就中国目前前端对方向键的支持情况来看,即使你呕心沥血做了个可以通过方向键控制下拉显示的导航菜单,再即使你昭示天下此导航可方向键操作,有几个人鸟你的账呢?所以我说,在中国,在目前,方向键的可用性虽然看上去不错,实际上没有多大价值。
其次是开发的可行性,要知道,大部分的公司的资源人力有限,而且很多人能力有限,你说,花巨大的代码成本搞个可用性很好(但受众很小的)的下拉菜单,有必要吗?可能吗?你会吗?

所以说,上面yahoo开发和Mozilla首页的下拉的键盘方法是没有任何实际应用价值的,其意义仅在于学习、提高JavaScript水平,尤其在控制键盘事件上。

您可能会疑问,照我的结论,所谓“下拉菜单可访问性”只是个空洞的理论,没有实际价值嘛。哦,其实不然,我们其实可以找到即有实际应用价值,同时又开发简单常用,又能提高页面键盘可用性的方法的。其关键就是focus事件。作为键盘用户,最常用的是哪个键,一个是Enter回车键,另外一个就是Tab焦点切换键。Tab键可以让页面上的a标签以及表单元素(button, input)依次获取焦点(如果没有设置tabindex属性的话),当Tab键切换到某个元素时,此元素其实已经相应了onfocus方法(大部分情况下未定义方法),此时如果按下Enter回车键,其实相对于触发了onclick方法,如果a标签有链接的话,会打开链接的。

所以,我们可以抛开烦人的方向键,而专注于Tab键,以及Tab切换触发的focus方法。我们再添加一个让菜单获取焦点的显示下拉的方法,其不是大大提高了页面的键盘可访问性。而且onfocus方法本身与键盘无关,我们只需要专注于事件本身,而忽略键盘,所以开发成本是低的,非常可行的。

下面,就以mtime时光网的下拉为例,添加Tab键的下拉方法(其实是onfocus方法),看如果通过添加几行额外的代码,来大大提高页面的可访问性的。

五、给菜单添加focus事件增加键盘可用性

什么元素支持focus事件呢,据我所知为a标签,以及表单元素。所以,还是Mtime时光网的例子,我们要在下图所标示位置的a标签上添加focus方法,让其获取焦点时也能显示下拉菜单:
a标签添加focus方法 张鑫旭-鑫空间-鑫生活

由于鼠标经过也实现同样的下拉效果,所以,我们需要将显示下拉菜单的方法提出来,一番处理后,js代码如下:

<script>
    var o = document.getElementById("navigationRegion").getElementsByTagName("li");
    var l = o.length, cache;
	
    var menuTaga = function(obj){
        //获取a标签DOM对象
        return obj.getElementsByTagName("a")[0];
    }, menuDown = function(obj){
        var cl = obj.className;
        if(/_on$/.test(cl) || /_hover$/.test(cl)){
            return;          
        }
        obj.className = obj.className + "_hover"; 
    }, menuUp = function(obj){
        obj.className = obj.className.replace("_hover", "");
    }, menuCache = function(obj){
        if(cache){
            menuUp(cache);    
        }
        cache = obj;
    }, menuEvent = function(obj){
        obj.onmouseover = function(){
            menuCache(this);
            return menuDown(this);
        };
        obj.onmouseout = function(){
            return menuUp(this);
        };
        menuTaga(obj).onfocus = function(){
            menuCache(obj);
            return menuDown(obj);
        };
    };
    for(var i=0; i<l; i+=1){
        menuEvent(o[i]);
    }
</script>

上述方法除了focus方法之外,为了让下拉显示的列表也能通过Tab键访问,所以,对当前显示的下拉对象cache了下,以能够准确收起之前展开的下拉菜单。
上述脚本代码的效果如下图所示,通过切换Tab键显示的下拉列表内容:

添加键盘可用性的下拉菜单效果 张鑫旭-鑫空间-鑫生活

这里只看图是体会不出这里可访问性是如何切实提高的,您可以狠狠地点击这里:Tab键可访问的下拉菜单demo

无论是Firefox浏览器还是IE浏览器,不停的切换Tab切换键,当导航中的a标签获取焦点时(出现虚框,Chrome土橙色框,Safari蓝框)(可见outline是可访问性里面很重要的东西,没事就设置outline:none的人都是不明真相的盲从者),就会出现下拉菜单。此时下拉菜单中的列表项也可以通过Tab键获取(如果下拉菜单不显示,菜单列表是无法通过Tab键访问的)。

于是,对于那些无法或不想使用鼠标的用户而言,这里导航的可访问性显然更高一筹,因为,他们可以轻松使用键盘访问导航里面的每一项内容,Tab切换,回车键访问。

值得一提的是,对于Safari浏览器用户,默认Tab切换元素是不高亮的,您需要在偏好设置里勾选此项,如下图所示:
Safari浏览器设置Tab切换元素高亮 张鑫旭-鑫空间-鑫生活

六、抓住最后一点尾巴

我知道“站着说话不腰疼”,放眼看看中国互联网上的这些网站,绝大多数网站连个小众用户都没有,更别谈考虑到“键盘偏好用户”了,人力有限,财力有限,所谓的这些可访问性问题都只是些用钱的公司,有钱的网站有资本把玩的东西。成千上万生死先上挣扎的中小网站,对于他们来讲,本文的意义又何在呢?

我想,应该不少同行看过本文,知道了下拉菜单这类东西最好添加键盘可访问的方法。但是多仅局限于只是知道而已,真正去付诸实践的有多少呢,我是很怀疑的。所以从这点来讲,本文的实际价值又似乎打了折扣了。所以,我自己并不奢望每个前端开发人员日后都开始重视键盘用户的可访问性问题,要为页面重要的地方写键盘事件,或者是本文重点演示的focus事件。

然而,虽然,我觉得为少了的键盘用户、屏幕阅读器用户额外写js方法在大多数网站是行不通的。但是,其实只要举手之劳,我们也能够让自己网站的可访问性更好,而这些看上去不起眼的操作实际上是更加有实际应用与推广价值的,到底哪些“举手之劳”呢?

首先,把网站CSS reset中的outline:0;,痛痛快快,舒舒服服地删掉。
其次,凡事涉及到事件的元素,例如按钮,选项卡等。都必须使用(或包含)a标签或是表单元素(如button)。这不难理解,就拿本文时光网导航举例,实际上导航中的a标签去掉,同样的脚本,已经可以实现下拉菜单的效果了。或许有人反而会认为自己仅仅使用了ul, li标签就实现下拉菜单效果沾沾自喜,认为自己去掉了荣誉的a标签,HTML代码够简洁,这就跟《媳妇的美好时代》里面的毛峰一样,家里这个温柔贤惠漂亮勤劳的老婆不要,跑去沾惹那个什么龙瑾,不要是看外表风骚,看知道内涵才行。

所以,我要看到只要ul, li标签的导航觉得苗条性感,实际上只是个花花瓶子,里面需要有点内涵的东西——a标签。a标签本身的存在就是提高的页面的可访问性了。

所以呢,那些自以为代码精简、或是自认为a标签虚框不好看而使用类似span标签代替的做法,都是业余的,是可以轻松改进的。

然后,不要自以为的不要自以为边缘处理后的文字似乎更好看,就把按钮,选项卡的文字用图片代替,连文字也不留两个。你说屏幕阅读器认识图片吗,还有,你就这么喜欢没事来两个图片链接请求来虐待服务器吗?所以,少用图片代替文字,即使要代替,也要把文字藏在后面,注意是藏在后面,就是图片不显示文字就显示,而不是隐藏掉。

最后,总结下,就是outlinea标签,文字。如果您想做真正优秀的网站,加一条,键盘响应事件。

七、结语加《盗梦空间》电影之旅

本来这篇文章预计9点就可以发布,结果拖到现在,为什么呢?啊,说来话长,当时我写文章正进入体验高峰状态,突然外面有人敲窗户,我以极慢的速度转过头,
盯着黑乎乎的窗外看了10秒钟,眼睛共慢悠悠地眨了3下(1~2~3~),突然,眼前一亮,啊!这不是芳芳同学吗?我故作镇静的走过去,打开窗户,“什么事?”只见其玉手拿出两张黄黄的票子,说:“我这儿有两种《盗梦空间》的电影票,多了一张,你要不要去看?”此言一出,我肾上腺素立即升高,“哇靠,这不是电视剧里才有的桥段吗?”这等美事怎能放过,于是乎,我平生第二次和女生单独看电影(第一次是和我妹)。不要问我后来怎么样了,我现在脑子里有的就是:啊,电影很好看;啊,陆家嘴很美;啊,我的心情超好。

呼~~长嘘一口气,回到本文,关于本文……果然,还是不行,心情依旧沉浸在喜悦之中。算了,有什么问题评论交流。就这些,洗洗睡了~~

(本篇完)

分享到:


发表评论(目前11 条评论)

  1. 赵明亮说道:

    大神,又来你博客了

  2. 小蜜蜂说道:

    看你的文章果然有收获,谢谢!

  3. Jonwade说道:

    哈哈 芳芳同学是不是现在的女友啊

  4. 付正宗说道:

    要是能再加上方向键来控制下拉菜单里的选项就更好了,我一开始以为用tab键进入到下拉菜单后通过方向键可以继续往下选,没想到不可以……

  5. SkyWoo说道:

    博主,你太牛了!我要向你学习!

  6. 呵呵,对于HTML5技术我还真的是没怎么了解过!!!

  7. 康康说道:

    Jace,这样,手动设置a标签的tabindex就好了,麻烦归麻烦,但是很实用。

  8. Jace说道:

    其实无障碍问题随便翻一本国外CSS入门书籍,里面都会讲到,国内的作者们都为了实现效果把这部分忽略了,形成良好的代码书写习惯,也就不会认为这是额外的成本。
    对于你提到的隐藏文本,我也借宝地说说:隐藏文本请不要用display:none;或者visibility:hidden;实现,读屏软件读不出这些文本,也就起不到辅助作用。
    另外你的这个demo很赞,不过存在一个问题:必须tab完一个导航的所有选项才能跳到下一个导航,如果选项较多会比较浪费时间。据我的了解,视障用户对方向键的使用是很频繁的。

  9. 草根网说道:

    好文,收藏至20ju.com

  10. 宇义说道:

    文章不错,说到了很多无奈和现实。

    楼上说到了vimperator,话说在这个页面上vimperator的操作都不能用了,不知何因。

  11. 澈言说道:

    不知鑫旭有没有试过Firefox下的vimperator插件,左右键盘右手鼠标,手腕很容易痛,所以安装了这个插件,用键盘控制网页,这样一来像很多A:Hover这样的鼠标指向效果就都消失了,所以很多指向性的下拉菜单就都不能通过键盘打开了……
    你这到是个好方法。