JS 自定義組件


經常會用到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=[];\n',
            cursor = 0;
        var match;
        var add = function(line, js) {
            js ? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
                (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
            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(/[\r\t\n]/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; }, width: 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="width: 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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM