最近做的一個項目中有個功能是滑鼠移動到某一塊id為A,然後A的子項目id為B在下方緊貼著父元素A顯示出來,B裡面的內容是滑鼠移動到A元素上才請求服務端擷取資料。
我使用了mouseover,mouseout事件,採用jquery的on方法。大致如下
$(document).on("mouseover","#A",function(){ $(this).find("#B").css({"display":"block"}); $.get("***",null,function(data){ $(this).find("#B").html(data); })});$(document).on("mouseout","#A",function(){ $(this).find("#B").css({"display":"none"});});
顯示大概是這樣
可是TMD我發現我滑鼠移動到A上出現B後,滑鼠在兩者間滑動,網路在不停的請求子項目的資料,也就是說在不停的觸發A元素的mouseover事件,然後頁面中就卡幾下。
當時項目發布比較急,就沒管,後來周末一大早,心頭很悶,大冬天爬起來洗個澡開啟電腦就開始研究這個問題。
第一步:
代碼:
<script type="text/javascript" src="jquery.comjquery-1.10.2.min.js"></script><style>#father{width:200px;height:200px;background-color:red;position:relative;}#child{display:none;position:absolute;left:0;top:199px;width:100px;height:100px;background-color:yellow;}#record{width:300px;height:100px;border:1px solid blue;position:absolute;right:0;top:0;}</style><script>$(document).on("mouseover","#father",function(e){$("#record").append("father mouseover</br>");$(this).find("div").css({"display":"block"});});$(document).on("mouseout","#father",function(e){$("#record").append("father mouseout</br>");$(this).find("div").css({"display":"none"});});</script><div id="father"><div id="child" ></div></div><div id="record" ></div>
我的滑鼠從father元素滑入,出現了child元素,再從father元素滑到child元素中,出現如下情況
也就是說father元素先觸發mouseover事件,當滑鼠進入child元素後觸發了mouseout事件,並且再次觸發了mouseover事件。
這種情況只有一種解釋,就是從father滑入child時,father元素觸發了mouseout事件的,進入child元素觸發了child的mouseover事件,並且通過事件冒泡,進而觸發了father元素的mouseover事件。
第二步:
為了證實上述的解釋,接下來做另一個實驗。
<script type="text/javascript" src="jquery.comjquery-1.10.2.min.js"></script><style>#father{width:200px;height:200px;background-color:red;position:relative;}#child{display:none;position:absolute;left:0;top:199px;width:100px;height:100px;background-color:yellow;}#record{width:300px;height:100px;border:1px solid blue;position:absolute;right:0;top:0;}</style><script>$(document).on("mouseover","#father",function(e){$("#record").append("father mouseover</br>");$(this).find("div").css({"display":"block"});});$(document).on("mouseout","#child",function(e){e.stopPropagation();$("#record").append("child mouseout</br>");});$(document).on("mouseover","#child",function(e){e.stopPropagation();$("#record").append("child mouseover</br>");});$(document).on("mouseout","#father",function(e){$("#record").append("father mouseout</br>");$(this).find("div").css({"display":"none"});});</script><div id="father"><div id="child" ></div></div><div id="record" ></div>
出現如下記錄
動作和第一步中的動作一致,但是我使用了阻止冒泡,所以father元素就沒有觸發了第二次的mouseover事件,並且此時child元素由於father元素觸發了mouseout事件將child的display設為none,並且在child觸發了mouseover事件時沒有冒泡觸發father的此事件,所以沒有將child的display設為block,所以child不再顯示了。
也就是說當滑鼠在father和child之間滑動時,是通過father不斷的觸發mouseover事件才使得child元素一直顯示,其實是child一直在迴圈消失又再顯示的過程,肉眼無法分辨,所以看到child會一直顯示。
是不是這樣呢?我們接著看
第三步:
代碼:
<script type="text/javascript" src="jquery.comjquery-1.10.2.min.js"></script><style>#father{width:200px;height:200px;background-color:red;position:relative;}#child{display:none;position:absolute;left:0;top:199px;width:100px;height:100px;background-color:yellow;}#record{width:300px;height:100px;border:1px solid blue;position:absolute;right:0;top:0;}</style><script>$(document).on("mouseover","#father",function(e){$("#record").append("father mouseover</br>");$(this).find("div").css({"display":"block"});});$(document).on("mouseout","#child",function(e){//e.stopPropagation();$("#record").append("child mouseout</br>");});$(document).on("mouseover","#child",function(e){//e.stopPropagation();$("#record").append("child mouseover</br>");});$(document).on("mouseout","#father",function(e){$("#record").append("father mouseout</br>");$(this).find("div").css({"display":"none"});});</script><div id="father"><div id="child" ></div></div><div id="record" ></div>
記錄如下:
果然,我的動作依然是從father滑入,然後向下滑入到child元素內,觸發的事件如所示。
到這裡我已經明白我項目中的那個問題產生的原因,但是怎麼解決呢,我希望在A和B元素間滑動時不要再重複請求服務端資料,可是A的mouseover事件在一直觸發,怎麼辦呢?突然我想起了target....
第三步:
代碼:
<script type="text/javascript" src="jquery.comjquery-1.10.2.min.js"></script><style>#father{width:200px;height:200px;background-color:red;position:relative;}#child{display:none;position:absolute;left:0;top:199px;width:100px;height:100px;background-color:yellow;}#record{width:300px;height:100px;border:1px solid blue;position:absolute;right:0;top:0;}</style><script>$(document).on("mouseover","#father",function(e){$("#record").append("father mouseover,target:"+e.target.id+"</br>");$(this).find("div").css({"display":"block"});});$(document).on("mouseout","#child",function(e){//e.stopPropagation();$("#record").append("child mouseout,target:"+e.target.id+"</br>");});$(document).on("mouseover","#child",function(e){//e.stopPropagation();$("#record").append("child mouseover,target:"+e.target.id+"</br>");});$(document).on("mouseout","#father",function(e){$("#record").append("father mouseout,target:"+e.target.id+"</br>");$(this).find("div").css({"display":"none"});});</script><div id="father"><div id="child" ></div></div><div id="record" ></div>
同樣的滑鼠移動操作,產生結果如下:
target是某次事件觸發的原始元素,也就是第一個觸發此事件的元素,然後冒泡,引起上階項目觸發此事件。
中清晰記錄了事件觸發的源頭。“father mouseover,target:child”這條記錄說明father的mouseover事件觸發的源頭是child。
那麼在我的項目中的那個問題,我可通過觸發事件的源頭來決定是否請求服務端的資料,修改大致如下:
$(document).on("mouseover","#A",function(e){ $(this).find("#B").css({"display":"block"}); var flag=parseInt($("#C").val()); if(e.target.id=="A"){ $.get("***",null,function(data){ $(this).find("#B").html(data); }) }});$(document).on("mouseout","#A",function(e){ $(this).find("#B").css({"display":"none"}); $("#C").val("true");});