用js的eval函數模擬Web API中的onclick事件
阿新 • • 發佈:2018-09-29
腳本 ply function img listen target list getattr 部分
在檢查組內小夥伴提交的tabToggler插件的js代碼時,發現了onclick的如下用法:
el.onclick = function(){ //按鈕樣式切換 for(var i=0;i<obj.btns.length;i++){ obj.btns[i].classList.remove("current"); } this.classList.add("current");//內容顯示切換 for(var j = 0;j<obj.divs.length;j++){ obj.divs[j].style.display = ‘none‘; } obj.divs[this.index].style.display = ‘block‘; console.log(this.index); }
顯然這個this指向事件源對象—eventTarget,最近一直在鉆研閉包和this的我,看到這裏覺得非常的怪,為什麽這裏的this可以指向eventTarget呢,而且在書寫在html標簽裏的事件處理函數也可以在非閉包作用域下直接訪問this(指向target),如下代碼:
<div id="tab1" myonclick = "console.log(this.id);console.log(this)">Tab1</div>
打印結果:
這到底怎麽實現的?按照需求一步一步分析就可以了,其實onclick的事件處理函數包括了兩部分:
Part 1: 來自html標簽內的onclick屬性內的js代碼;
Part 2: 來自己js腳本裏的onclick方法
需求如下:
Part 1 裏的onclick字符串可以被click事件執行,而且this作用域是eventTarget
問題:js裏有什麽機制能執行字符串形式的js代碼?如何讓代碼作用域指向eventTarget?
答案:eval,且eval執行本身就有作用域的概念,讓代碼的作用域指向eventTarget就行了。
Part 2 裏的onclick方法被click事件執行內部邏輯,而且this作用域是eventTarget;
綜上,我們可以用eval和指定作用域的辦法來擴展一個myonclick時間了,具體實現如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="tab1" myonclick = "console.log(this.id);console.log(this)">Tab1</div> <div id="tab2">Tab2</div> </body> <script> //擴展HTMLDivElement原型 HTMLElement.prototype.clickHandle = function(){ //1.檢查dom標簽裏有myonclick屬性 var _evalClickStr = this.getAttribute( "myonclick" ); //2.檢查dom有無指定myonclick方法 if( !!this.myonclick && typeof this.myonclick === "function" ){ var _clickHandle = this.myonclick; } var nativeEventBind = this.addEventListener || this.attachEvent; if( !!nativeEventBind ){ nativeEventBind( ‘click‘,(function( e ){ //執行eavl代碼串 eval( _evalClickStr ); //執行myonclick事件處理函數 _clickHandle.apply( this,e ); }).bind(this) ); } else return; } var tab1 = document.getElementById( "tab1" ); tab1.myonclick = function(){ console.log( this.id ); }; tab1.clickHandle(); </script> </html>
核心設計要點:擴展了HTMLElement.prototype對象,在clickHandle方法裏,指定了兩部分onclick代碼的執行上下文為HTMLElement元素本身。
用js的eval函數模擬Web API中的onclick事件