如何让标签onclick事件优先于jQuery focusout事件执行?
这个问题我之前也踩过坑!浏览器的事件触发顺序在这里搞了个小乌龙——当你点击下拉菜单里的标签时,输入框的focusout事件会先于标签的click事件触发,因为点击标签的瞬间输入框已经失去焦点了,结果就是菜单被提前清空,点击事件根本没机会执行。下面给你几个实用的解决办法,结合你的代码来改:
方案1:给Focusout加延迟,给Click留执行时间
最简单的修改方式,就是在focusout的清空操作外面套一层setTimeout,让浏览器先处理完点击事件再执行清空:
$("#reg_c_interest_editor").focusout(function () { // 延迟100ms执行清空,足够click事件完成 setTimeout(function() { $("#reg_c_interest_queryresult").empty(); }, 100); });
这个方法优点是改动极小,几乎不需要调整现有代码;缺点是依赖固定的时间延迟,虽然100ms在绝大多数场景下都够用,但理论上存在极端情况(比如页面卡顿)导致延迟不够的可能。
方案2:用Mousedown代替Click事件
浏览器的事件触发顺序里,mousedown是早于focusout的。把标签的点击事件换成mousedown,就能确保先执行add_tag,再处理输入框的失焦清空:
修改你循环里的事件绑定部分:
$.each(data.countries, function(i, item) { var listItem = document.createElement("li"); var tagItem = document.createElement("span"); tagItem.innerHTML = item; // 把onclick换成onmousedown tagItem.onmousedown = function() { add_tag(item); }; tagItem.style.cursor = "pointer"; tagItem.className = "reg_c_interest_tag"; listItem.appendChild(tagItem); document.getElementById("reg_c_interest_queryresult").appendChild(listItem); });
同时保留原来的focusout代码不变就行。这个方案比延迟更可靠,因为完全依赖事件的原生触发顺序,没有时间差的问题。
方案3:判断失焦后的焦点是否在菜单内(最健壮)
如果想做到最严谨,可以在focusout里检查:失焦后,用户的焦点是不是落在了下拉菜单的元素上。如果是,就不清空菜单;如果不是(点击了页面其他地方),再清空:
$("#reg_c_interest_editor").focusout(function () { // 用0延迟让浏览器先完成焦点切换,再判断当前焦点位置 setTimeout(function() { var activeElement = document.activeElement; // 检查当前焦点是否在查询菜单内部 if (!$("#reg_c_interest_queryresult").has(activeElement).length) { $("#reg_c_interest_queryresult").empty(); } }, 0); });
这个方案最健壮,能完美区分“点击菜单标签”和“点击页面其他地方”两种场景,适合对交互体验要求高的情况。
顺便:用jQuery简化事件绑定
你提到想使用jQuery的click绑定,其实可以把原生创建元素的代码改成jQuery风格,代码更简洁,也更符合jQuery的使用习惯:
$.each(data.countries, function(i, item) { // 用jQuery创建标签元素 var $tagItem = $("<span>") .text(item) .addClass("reg_c_interest_tag") .css("cursor", "pointer") .click(function() { add_tag(item); }); // 创建li并追加到菜单 $("<li>").append($tagItem).appendTo("#reg_c_interest_queryresult"); });
以上几种方案都能解决你的问题,根据自己的需求选就行——追求快改选方案1,追求可靠选方案2,追求完美选方案3~
内容的提问来源于stack exchange,提问作者ShellRox

