经常会用到JS插件,但从未研究过插件的写法
目前主流的写法有多种,各有各的优缺点,下面,我就以最常规的一种写法举例
// plugin.js ;(function(undefined) {//防止出现undefined问题 "use strict" var _global; // 工具函数 // 对象合并 function extend(o,n,override) { for(var key in n){ if(n.hasOwnProperty(key) && (!o.hasOwnProperty(key) || override)){ o[key]=n[key]; } } return o; } // 自定义模板引擎 function templateEngine(html, data) { var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[]; ', cursor = 0; var match; var add = function(line, js) { js ? (code += line.match(reExp) ? line + ' ' : 'r.push(' + line + '); ') : (code += line != '' ? 'r.push("' + line.replace(/"/g, '\"') + '"); ' : ''); return add; } while (match = re.exec(html)) { add(html.slice(cursor, match.index))(match[1], true); cursor = match.index + match[0].length; } add(html.substr(cursor, html.length - cursor)); code += 'return r.join("");'; return new Function(code.replace(/[ ]/g, '')).apply(data); } // 通过class查找dom if(!('getElementsByClass' in HTMLElement)){ HTMLElement.prototype.getElementsByClass = function(n){ var el = [], _el = this.getElementsByTagName('*'); for (var i=0; i<_el.length; i++ ) { if (!!_el[i].className && (typeof _el[i].className == 'string') && _el[i].className.indexOf(n) > -1 ) { el[el.length] = _el[i]; } } return el; }; ((typeof HTMLDocument !== 'undefined') ? HTMLDocument : Document).prototype.getElementsByClass = HTMLElement.prototype.getElementsByClass; } // 插件构造函数 - 返回数组结构 function MyDialog(opt){ debugger; this._initial(opt); } MyDialog.prototype = { constructor: this,//这种原型链写法必须要加上constructor _initial: function(opt) { // 默认参数 var def = { ok: true, ok_txt: '确定', cancel: false, cancel_txt: '取消', confirm: function(){}, close: function(){}, content: '', tmpId: null }; debugger; this.def = extend(def,opt,true); //配置参数 this.tpl = this._parseTpl(this.def.tmpId); //模板字符串 this.dom = this._parseToDom(this.tpl)[0]; //存放在实例中的节点 this.hasDom = false; //检查dom树中dialog的节点是否存在 this.listeners = []; //自定义事件,用于监听插件的用户交互 this.handlers = {}; }, _parseTpl: function(tmpId) { // 将模板转为字符串 var data = this.def; var tplStr = document.getElementById(tmpId).innerHTML.trim(); return templateEngine(tplStr,data); }, _parseToDom: function(str) { // 将字符串转为dom var div = document.createElement('div'); if(typeof str == 'string') { div.innerHTML = str; } return div.childNodes; }, show: function(callback){ var _this = this; if(this.hasDom) return ; debugger;
//自定义事件监听 if(this.listeners.indexOf('show') > -1) { if(!this.emit({type:'show',target: this.dom})) return ; } document.body.appendChild(this.dom); this.hasDom = true; this.dom.getElementsByClass('close')[0].onclick = function(){ _this.hide(); if(_this.listeners.indexOf('close') > -1) { _this.emit({type:'close',target: _this.dom}) } !!_this.def.close && _this.def.close.call(this,_this.dom); }; this.dom.getElementsByClass('btn-ok')[0].onclick = function(){ _this.hide(); if(_this.listeners.indexOf('confirm') > -1) { _this.emit({type:'confirm',target: _this.dom}) } !!_this.def.confirm && _this.def.confirm.call(this,_this.dom); }; if(this.def.cancel){ this.dom.getElementsByClass('btn-cancel')[0].onclick = function(){ _this.hide(); if(_this.listeners.indexOf('cancel') > -1) { _this.emit({type:'cancel',target: _this.dom}) } }; } callback && callback(); if(this.listeners.indexOf('shown') > -1) { this.emit({type:'shown',target: this.dom}) } return this; }, hide: function(callback){ if(this.listeners.indexOf('hide') > -1) { if(!this.emit({type:'hide',target: this.dom})) return ; } document.body.removeChild(this.dom); this.hasDom = false; callback && callback(); if(this.listeners.indexOf('hidden') > -1) { this.emit({type:'hidden',target: this.dom}) } return this; }, modifyTpl: function(template){ if(!!template) { if(typeof template == 'string'){ this.tpl = template; } else if(typeof template == 'function'){ this.tpl = template(); } else { return this; } } this.dom = this._parseToDom(this.tpl)[0]; return this; }, css: function(styleObj){ for(var prop in styleObj){ var attr = prop.replace(/[A-Z]/g,function(word){ return '-' + word.toLowerCase(); }); this.dom.style[attr] = styleObj[prop]; } return this; }, function(val){ this.dom.style.width = val + 'px'; return this; }, height: function(val){ this.dom.style.height = val + 'px'; return this; }, on: function(type, handler){ // type: show, shown, hide, hidden, close, confirm if(typeof this.handlers[type] === 'undefined') { this.handlers[type] = []; } this.listeners.push(type); this.handlers[type].push(handler); return this; }, off: function(type, handler){ if(this.handlers[type] instanceof Array) { var handlers = this.handlers[type]; for(var i = 0, len = handlers.length; i < len; i++) { if(handlers[i] === handler) { break; } } this.listeners.splice(i, 1); handlers.splice(i, 1); return this; } }, emit: function(event){ debugger; if(!event.target) { event.target = this; } debugger; if(this.handlers[event.type] instanceof Array) { var handlers = this.handlers[event.type]; for(var i = 0, len = handlers.length; i < len; i++) { handlers[i](event); return true; } } return false; } } // 最后将插件对象暴露给全局对象 _global = (function(){ return this || (0, eval)('this'); }()); if (typeof module !== "undefined" && module.exports) { module.exports = MyDialog; } else if (typeof define === "function" && define.amd) { define(function(){return MyDialog;}); } else { !('MyDialog' in _global) && (_global.MyDialog = MyDialog); } }());
调用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" type="text/css" href="index.css"> </head> <body> </body> <script type="text/template" id="dialogTpl"> <div class="mydialog"> <span class="close">×</span> <div class="mydialog-cont"> <div class="cont"><% this.content %></div> </div> <div class="footer"> <% if(this.cancel){ %> <span class="btn btn-ok"><% this.ok_txt %></span> <span class="btn btn-cancel"><% this.cancel_txt %></span> <% } else{ %> <span class="btn btn-ok" style=" 100%"><% this.ok_txt %></span> <% } %> </div> </div> </script> <script src="MyDialog.js" type="text/javascript"></script> <script> var mydialog = new MyDialog({ tmpId: 'dialogTpl', cancel: true, content: 'hello world!' }); mydialog.on('confirm',function(ev){ debugger; console.log('you click confirm!'); // 写你的确定之后的逻辑代码... }); mydialog.show(); </script> </html>