经常会用到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>