打印

HO2009: 各浏览中对 MAP 和 AREA 元素的事件处理行为不同

作者:钱宝坤

标准参考

MAP 和 AREA 元素通常组合起来使用为图片要设置一个超链接区域。使用时将 IMG 元素的 "usemap" 属性1关联到一个 MAP 元素上,这个 MAP 元素的 "name" 属性值要与 IMG 元素的 "usemap" 属性值相同。AREA 元素嵌入在 MAP 元素中,用来指定该图片中超链接区域的形状 ("shape") 、所处位置 ("coords")、链接地址 ("href") 等。

HTML4.01 规范中对 AREA 元素的 "shape" 和 "coords" 属性的描述如下:

  • shape:可取值为 default | rect | circle | poly,用来指定超链接区域的形状。
    • default:将整个图片定义为超链接区域;
    • rect:定义一个矩形超链接区域;
    • circle:定义一个圆形超链接区域;
    • poly:定义一个多边形超链接区域;
  • coords:用来指定超链接区域在屏幕上的位置和形状,确定位置的坐标值和坐标值的书写顺序由所设定的形状来决定,确定位置与形状的可能性组合如下
    • rect:left-x,top-y,right-x,bottom-y ,通过两个点来确定一个矩形的左上角与右下角的坐标,从而确定一个矩形区域;
    • circle:center-x, center-y, radius ,通过一对坐标值确定一个点,并以这个点为圆心,"radius" 值为半径确定一个圆形的超链接区域,当 "radius" 值为百分数时,依照被关联对象的 min(width,height)计算出半径的数值;
    • poly:x1, y1, x2, y2, ..., xN, yN ,每两个为一对坐标用来确定一个点,当这些点的坐标值不全相同时,以第一个点为该多边形的起始点,最后一个点为多边形的终点,将这些点连接起来围成一个多边形;

关于 MAP 和 AREA 元素的更多内容,请参考 CSS2.1 规范 13.6.1 Client-side image maps: the MAP and AREA elements 中的内容。

注 1:要关联 MAP 元素的 IMG 元素的 "usemap" 属性值必须以 "#" 开头并且加上要关联的 MAP 元素的 "name" 属性值。

问题描述

MAP 元素会影响指向 MAP 的元素,其父元素 A 元素的默认链接行为。

IE6 IE7 IE8 中,产生自 MAP 元素的事件冒泡路径,不依赖 MAP 本身所处的元素嵌套规则,他会执行到引向 MAP 的元素嵌套结构中。

造成的影响

页面无法按照预期结果执行导航,并且意外的触发原本不应在事件冒泡路径上的事件处理程序。

受影响的浏览器

所有浏览器  

问题分析

1. 对 A 元素默认链接行为的阻止

分析以下代码:img-map.html

<a href="http://www.google.com" target="_blank">
  <img src="Chrome1.png" usemap="#Map1" style="border:none" /> text
</a>
<map name="Map1"></map>

A 元素链接到 Google 站点,元素内嵌套 IMG 标签,此图片使用 "usemap" 属性关联到名为 "Map1" 的 MAP 元素中。

分别点击 A 元素的图片区域及文本区域,各浏览器实际结果处理如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
点击图片区域 失效 有效
点击文本区域 有效 有效

可见,在 IE6 IE7 IE8 中,如果 A 元素内带有使用 usemap 属性关联了 MAP 元素的 IMG 元素,将导致超链接中该图片区域默认行为失效。

 

2. MAP AREA 元素的事件冒泡机制差异

分析以下两组代码:map-area_event_bubble.html

<script>
  function clicked(i, o) {
    document.getElementById('info' + i).innerHTML += o.tagName + ' Tag Clicked.<br />';
  }
</script>

<div>
  MAP 位于 A 之外,请点击图片:<br />
  <a href="#" onclick="clicked(1, this)">
    <img src="Chrome1.png" border="0" usemap="#Map1" onclick="clicked(1, this)" />
  </a>
  <map name="Map1" onclick="clicked(1, this)">
    <area shape="rect" coords="0,0,90,90" onclick="clicked(1, this)">
  </map>
</div>
<div id="info1"></div>
<br />
<div>
  MAP 位于 A 之内,请点击图片:<br />
  <a href="#" onclick="clicked(2, this)">
    <img src="Chrome1.png" border="0" usemap="#Map2" onclick="clicked(2, this)" />
    <map name="Map2" onclick="clicked(2, this)">
      <area shape="rect" coords="0,0,90,90" onclick="clicked(2, this)">
    </map>
  </a>
</div>
<div id="info2"></div>

两组代码中均为 AREA MAP A IMG 元素写入内联 "click" 事件,第一组为 MAP 标签没有嵌套在 A 标签内情况,第二组为 MAP 标签嵌套在 A 元素内情况。

根据浏览器事件冒泡机制,点击 AREA 元素,在触发完成自身的 "click" 事件后,AREA 元素的父级元素直至到视口元素为止,都会依次触发他们的 "click" 事件。因此根据规范描述,第一组元素应会根据元素嵌套关系,实现事件冒泡机制,依次触发 AREA MAP 元素的 "click" 事件。而第二组则是,依次触发 AREA MAP A 元素的 "click" 事件。

尝试点击 AREA 元素,各浏览器实际结果处理如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
MAP 位于 A 之外 AREA Tag Clicked.
MAP Tag Clicked.
IMG Tag Clicked.
A Tag Clicked.
AREA Tag Clicked.
MAP Tag Clicked.
MAP 位于 A 之内 AREA Tag Clicked.
MAP Tag Clicked.
IMG Tag Clicked.
A Tag Clicked.
AREA Tag Clicked.
MAP Tag Clicked.
A Tag Clicked.

可见,IE6 IE7 IE8 中即使 MAP 元素没有嵌套在 A 元素内,其事件冒泡机制依然会通过 IMG 元素关联起来,这显然是个不符合事件冒泡机制的实现扩展。

而其他浏览器则是严格根据元素实际嵌套关系产生事件冒泡。

 

3. MAP AREA 标签对 A 标签的默认行为支持差异

结合之前的第一和第二点差异,我们构造出如下两组用例:

<script>
  function clicked(i, o) {
    document.getElementById('info' + i).innerHTML += o.tagName + ' Tag Clicked.<br />';
  }
</script>

<div>
  MAP 位于 A 之外,请点击图片:<br />
  <a href="http://www.google.com" onclick="clicked(1, this)" target="_blank">
    <img src="Chrome1.png" border="0" usemap="#Map2" />
  </a>
  <map name="Map2">
    <area shape="rect" coords="0,0,90,90" onclick="clicked(1, this)">
  </map>
</div>
<div id="info1"></div>
<br />
<div>
  MAP 位于 A 之内,请点击图片:<br />
  <a href="http://www.google.com" onclick="clicked(2, this)" target="_blank">
    <img src="Chrome1.png" border="0" usemap="#Map1" />
    <map name="Map1">
      <area shape="rect" coords="0,0,90,90" onclick="clicked(2, this)">
    </map>
  </a>
</div>
<div id="info2"></div>

第 1 组代码中,MAP 处于 A 标签外,第 2 组 MAP 处于 A 标签内。

两组代码中均使用, A 元素嵌套 IMG 和 MAP 元素,IMG 元素使用 "usemap" 属性指向 MAP 元素, MAP 中使用 AREA 元素创造出足以覆盖 IMG 区域的链接区。

根据之前说明可知,在第 2 组用例中,点击图片区域,会先触发 "AREA" 元素的 "click" 事件。然后根据事件冒泡机制,会使父元素 A 也产生 "click"。A 元素内存在 "href" 属性并链接到 Google 首页上,被点击后应将该页导航到 Google。

运行用例,来看不同的浏览器中的表现结果汇总如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
第 1 组 A 元素导航失效 事件冒泡不会到达 A 元素,不产生导航行为。 事件冒泡不会到达 A 元素,不产生导航行为。
第 2 组 A 元素导航失效 A 元素导航失效 导航到 Google

根据上表可见:

  • IE6 IE7 IE8 中由于文章最开始说明的,如果 A 标签内包含有使用 MAP 的 IMG 标签,将导致 A 链接整体失效;
  • Firefox 中,即使点击事件冒泡到 A 标签中,也不会触发 A 标签的默认导航行为发生;
  • Chrome Safari Opera 均处理正常。

解决方案

  • 注意 MAP 标记与其他标记的嵌套关系,IE6 IE7 IE8 中的 MAP 冒泡机制是根据指向 MAP 标记的标记位置决定的。如果期望所有浏览器冒泡路径基本一致,可以将 MAP 标记放在引用 MAP 的标记的兄弟级别。
  • 注意 A 标记的默认导航行为触发限制,即使标签嵌套一致,点击事件冒泡路颈 A 标记,也不会在 IE6 IE7 IE8 Firefox 中触发浏览器默认行为产生导航。可以利用 A 标记也会执行点击事件冒泡的特性,使用 A 标记的 "click" 事件代替 "href" 属性执行页面跳转工作。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.11
Chrome 8.0.552.11 dev
Safari 5.0.2
Opera 10.63
测试页面: img-map.html
map-area_event_bubble.html
map-area_default.html
本文更新时间: 2010-10-25

关键字

AREA MAP A 事件冒泡 默认行为 链接 导航