逍遥游

表单按钮无反应,JS的冒泡模型导致的?

今天写网页碰到一个问题。

给按钮添加点击事件,然后跳转到其他网页,写完代码在firefox上测试通过后部署,却在手机上测试发现了问题,按钮点了之后根本没反应。想起浏览器是chrome,于是打开电脑上的chrome调试,发现同样没有反应。

html页面表单代码

1
2
3
4
5
6
7
8
9
<form class="form-signin" action="#">
<div class="input-group input-group-lg">
<label for="search-text" class="sr-only">Search</label>
<input type="text" id="search-text" class="form-control" required autofocus autocomplete="off" />
<span class="input-group-btn">
<button id="btn-search" type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></button>
</span>
</div>
</form>

监听事件代码

1
2
3
4
$("#btn-search").click(function(){
start($("#search-text").val());
//return false;
});

start函数,执行跳转操作

1
2
3
var start = function(content){
window.location.href = mirrorUrl.replace(/{query}/, content);
}

基本确定是代码问题,于是搜索为什么chrome下window.location.href没反应,找了好几个地方,终于解决了问题:在处理事件末尾加上return false

window.location.href无效/不跳转的原因分析

问题虽然解决了,但是仍然不知道为什么(web野路子伤不起啊)。本着打破沙锅问到底的精神,又一阵查找。看那篇文章似乎指的是JS的冒泡事件引起的,于是就从JS冒泡开始研究起。

事件的发生顺序

这个问题的起源非常简单,假设你在一个元素中又嵌套了另一个元素


| element1 |

| ————————- |

| |element2 | |

| ————————- |

| |


:并且两者都有一个onClick事件处理函数(event handler)。如果用户单击元素2,则元素1和元素2的单击事件都会被触发。但是哪一个事件先被触发?哪一个事件处理函数会被首先执行?换句话说,事件的发生顺序到底如何?

两种模型

不出所料,在那些“不堪回首”(浏览器大战)的日子里,Netscape和微软有两种截然不同的处理方法:

Netscape主张元素1的事件首先发生,这种事件发生顺序被称为捕获型

微软则保持元素2具有优先权,这种事件顺序被称为冒泡型

这两种事件顺序是截然相反的。Explorer浏览器只支持冒泡事件,Mozilla,Opera7和Konqueror两者都支持。而更古老的opera和iCab两者都不支持

冒泡型事件

当你使用冒泡型事件时

/ \

—————| |—————–

| element1 | | |

| ———–| |———– |

| |element2 | | | |

| ————————- |

| Event BUBBLING |


:元素2 的处理函数首先被触发,元素1其次
(摘自 生动详细解释javascript的冒泡和捕获,包懂包会

也就是说事件并不是仅执行了一个函数,而是继续向上传递,引起了其他事件。

拿我的例子做个说明。

首先,点击按钮触发自定义的监听事件,监听事件调用了start函数(打log发现确实执行了,但是没有跳转页面),然后告诉浏览器跳转页面。这里有一个问题,浏览器没有立即跳转(不然也没有那么多事了),而是先解析、连接目标网站,连接上之后才真正跳转,应该是浏览器为了避免用户长时间面对空白屏幕而做的优化。

准备跳转的同时,原网页的代码仍然可以执行,于是冒泡模型继续触发默认的事件:表单的submit事件,而表单的ACTION是“#”(本网页),于是浏览器就停止了跳转(事实上,它还没来得及发出解析请求)。

就这样,造成了submit按钮点击无反应的假象。

知道了原因,那么解决方案也就很简单,制止它的默认事件。在监听事件末尾加上return false即可

return false可以且仅可以阻止默认的事件,e.stopPropagation()可以取消所有冒泡事件,这样,子元素的事件就不会引起父元素的监听事件。

关于阻止冒泡,可以参考:浅谈 javascript 事件取消和阻止冒泡

以后一定要记得多加这么一句,不然真是坑死人。话说我的某一个网站也是这原因导致反馈请求无法处理(我说这学期改版后怎么一直没人反馈呢,汗)


多说停止服务,disqus引导注册太过分,暂时不上评论系统了。有机会自己造轮子吧。邮箱:input@newnius.com