Explication détaillée du framework front-end Heng.js

小云云
Libérer: 2023-03-21 08:16:02
original
3681 Les gens l'ont consulté

Description du framework : basé sur des idées de programmation orientée objet et implémenté en langage js natif, il ne s'appuie sur aucune bibliothèque js tierce et ne fait référence à aucun fragment de code externe ! Envisagez pleinement différents scénarios d’application pour garantir la flexibilité et la robustesse du framework.
* Fonction d'implémentation : elle implémente l'architecture et les détails internes qui sont totalement cohérents avec jQuery. La boucle interne de l'opération de configuration peut définir tous les nœuds correspondants et la méthode peut être appelée dans une chaîne.
* Classification des méthodes : les méthodes statiques sont utilisées dans le cadre de la logique des données, du prétraitement DOM et d'autres opérations globales d'instance non-DOM ; elles appellent les composants d'interface utilisateur correspondants ;
* Configuration des paramètres : configurez différents paramètres en fonction des besoins réels et appelez-les de manière flexible. S'il n'y a pas de paramètres, utilisez la configuration par défaut.
* Prise en charge du sélecteur : identifiant, classe, étiquette, sélecteur descendant, nœud dom, objet instancié H ("sélecteur css").
* Compatibilité : toutes les méthodes sont entièrement compatibles avec tous les navigateurs et compatibles avec la plupart des navigateurs actuels (IE6/7/8/9/10/11, Chrome, Firefox, Safari, etc.).
* Sécurité variable : dans un environnement de fermeture, le framework lui-même est garanti sûr et variable, et les conflits de variables sont évités.
* Performances du framework : optimisez le code pour améliorer les performances et minimiser les réorganisations et les redessins du navigateur. Libérez de la mémoire en attribuant une valeur nulle ; évitez la communication en îlot entre DOM, BOM et ECMAScript ; effectuez des opérations DOM basées sur des fragments de document ; * Méthode statique : H.Method(value)
* Méthode d'instance : H("css selector").Method(value), H("css selector").Method({key:value})
* H(sel).Method({configuration d'événement de propriété}), H(sel).Method(méthode du composant, paramètre de méthode)
* Appel de chaîne : H("css selector").Method({key :value}) .Method()
* Exécution après le chargement de l'arborescence DOM : H(fn) est équivalent à $(fn)===$(document).ready(fn)
* Boucle interne : La méthode d'instance exécute une boucle interne sur l'ensemble des nœuds correspondant par H ("sélecteur CSS"), et peut faire fonctionner tous les nœuds correspondants en même temps
* Méthode d'attribut : opts définit l'événement d'attribut du composant Méthode (méthode du composant, paramètre) ) ;
* Gesture tactile : encapsule les événements de gestes tactiles (Tap, Pinch, Hold, Swipe) pour le terminal mobile et signale l'état du geste correspondant via e.data. Tels que le coefficient de zoom par pincement, la direction de glissement, la distance de glissement, etc.
*
* Auteur : wheng


* Date : 2015-07-25
*/

* Code source du framework http://whwheng.gitee .io/csdn

*

*[Brève description structurelle]

* Le corps principal du cadre assure sa propre sécurité dans l'environnement de fermeture et évite les conflits variables vers l'extérieur ; via la classe de base H ou Heng Reference.

* Implémentez la fonction "css selector" dans le constructeur pour obtenir des nœuds DOM et stocker les nœuds sur l'attribut nodes, en suivant les règles de "l'attribution des données publiques".

(function(){
    ……框架主体……;
    window.H=window.Heng=H;//向外部引用基础类
})();
H(sel).Method()或Heng.(sel).Method();//外部实例化
 *内部已做了Heng基础类实例化处理new Heng(sel);实际使用时H(sel)即为创建对象,若sel为函数内嵌代码在形成完整DOM树时立即执行。

H(fn)实现了jqyuey库中的$(fn)===$(document).ready(fn)形成完整DOM树即可执行。

var H=function (sel){ 
    if(typeof sel=="function"){ H.ready(sel) }
    else{ return new Heng(sel); }
};
H(function(){ H(sel).Method();});//DOM树加载完毕H(fn)等价于jqyuey库中的$(fn)===$(document).ready(fn)
Copier après la connexion

*Les méthodes statiques ne nécessitent pas d'instanciation et sont utilisées pour la logique des données et le prétraitement DOM ou d'autres opérations globales non-DOM dans le cadre ; les méthodes d'instance sont combinées avec les nœuds DOM pour répondre aux exigences. L'entrée du composant correspondant sel est une collection, alors tous les nœuds peuvent implémenter la logique et les fonctions de la méthode.

function Heng(sel) {
    //【原选择器理】:选择器拆分数组,从document开始上级get后代作为下级祖先
    this.sel = sel;
    this.nodes = []; //选择器匹配的节点数组
    if (typeof sel == 'string') {
        if (sel.indexOf(' ') != -1) {
            var nodes = sel.split(' ');
            var childElements = [];
            var node = []; //实时祖先节点数组
            for (var i = 0; i < nodes.length; i++) {
                if (node.length == 0)
                    node.push(document);
                switch (nodes[i].charAt(0)) {
                case &#39;#&#39;:
                    childElements = []; //清除上一组值再更新
                    childElements.push(this.getId(nodes[i].substring(1)));
                    node = childElements;
                    break;
                case &#39;.&#39;:
                    childElements = [];
                    for (var j = 0; j < node.length; j++) {
                        var temps = this.getClass(nodes[i].substring(1), node[j]);
                        for (var k = 0; k < temps.length; k++) {
                            childElements.push(temps[k]);
                        }
                    }
                    node = childElements;
                    break;
                default:
                    childElements = [];
                    for (var j = 0; j < node.length; j++) {
                        var temps = this.getTagName(nodes[i], node[j]);
                        for (var k = 0; k < temps.length; k++) {
                            childElements.push(temps[k]);
                        }
                    }
                    node = childElements;
                }
            }
            this.nodes = childElements;
        } else { //sel为无空格选择器字符串
            switch (sel.charAt(0)) {
            case &#39;#&#39;:
                this.nodes.push(this.getId(sel.substring(1)));
                break;
            case &#39;.&#39;:
                this.nodes = this.getClass(sel.substring(1));
                break;
            default:
                this.nodes = this.getTagName(sel);
            }
        }
    } else if (typeof sel == &#39;object&#39;) { //sel为dom节点
        if (sel != undefined) {
            this.nodes[0] = sel;
        }
    }
}
Copier après la connexion

*La programmation orientée objet entraînera inévitablement une confusion de ce pointeur. Pour tous problèmes similaires, déclarez var This=this en dehors de la fonction via la fermeture, corrigez ce pointeur.

//静态方法
H.Method() = function (arg) {
    …code…
};

//实例方法
Heng.prototype.Method = function (opts) {
    ……
    var nodes = this.nodes[i]; //sel匹配的的节点数组
    for (var i = 0; i < this.nodes.length; i++) {//内部循环
        var op = this.nodes[i];
        var aUl = op.getElementsByTagName(&#39;ul&#39;);
        ……
    }
}
Copier après la connexion

*

Heng.prototype.slide = function (opts) {
    ……
    var This = this;
    node = function (opts) { //node为dom节点
        //原型上的方法getClass只能通过指向实例的this去引用,此时的this为node节点
        This.getClass(classString, parrent);
        /*this.getClass(classString,parrent)错误*/
     }
  }
 *每一个实例方法都可以通过修改参数对象otps实现不同的需求,未指定的数据会使用默认值。

//模态框
Heng.prototype.dialog = function (opts) {
    //opts={"animate":是否开启动画,"enterDir":进入方向,"maskopa":遮罩透明度,"warncount":警告闪烁次数,"content":弹框内容}
    var def = {
        "animate" : false,
        "enterDir" : "top",
        "maskbg" : "#000000",
        "maskopa" : 0.5,
        "warncount" : 5,
        "content" : "<h1>Hello World</h1>"
    };//def为默认值
    opts = H.extend(def, opts);//数据合并
    ……
}
Copier après la connexion
*[Cas de la méthode d'instance]

H("form").formCheck(); , et les fonctions sont les suivantes :

* Tout type d'éléments de formulaire dans tous les formulaires correspondant à H ("formulaire") sera vérifié.

*Chaque élément du formulaire sera validé indépendamment lorsqu'il perdra le focus.

* Lorsque le formulaire est soumis, tous les éléments internes sont vérifiés de manière uniforme, qui est divisée en "vérification individuelle (interrompant les inspections ultérieures en cas d'élément qui échouent à la vérification)" et "vérification unique ( vérifiant tous les éléments à la fois)" Deux modes, basculez entre les deux modes en configurant opts["submitCheck"]=true/false.

* Que la vérification soit réussie ou non, un message d'invite apparaîtra indiquant le succès ou l'échec du type de vérification correspondant. Vous pouvez configurer le paramètre opts pour personnaliser le contenu et les informations de style de l'invite. .

*Vérification intégrée des types de données courants, et le type de vérification peut également être étendu via opts["customType"] et opts["customReg"], ce qui améliore considérablement la flexibilité de la vérification des formulaires.

*

Heng.prototype.formCheck = function (opts) {
    //opts={"customType":"无formCheck的class值","customReg":"必须的自定义类型的正则文本","customTip":"必须的自定义类型的错误提示"}
    //自定义校验类型用"formCheck-"+opts["customType"]作为class值
    var def = {
        "user" : "*请输入3-16位字母数字",
        "password" : "*请输入5-17位以字母开头的字符数字组合",
        "email" : "*请输入正确邮箱地址",
        "Mobilephone" : "*请输入正确手机号",
        "radioBox" : "请选择",
        "ch" : "请输入中文字符",
        "wrongStyle" : "font-size:12px; color:#F00;",
        "passContent" : "成功",
        "passStyle" : "font-size:12px; color:#0C0;",
        "submitCheck" : false //提交时逐条验证还是一次验证显示所有错误信息。默认一次校验
    };
    opts = H.extend(def, opts);
    //dataType绑定到具体表单元素class值
    var dataType = ["formCheck-user", "formCheck-password", "formCheck-email", "formCheck-mobilePhone", "formCheck-ch", "formCheck-radioBox"];
    var Reg = {
        "user" : "^[a-zA-Z0-9_]{3,16}$",
        "password" : "^[a-zA-Z]+\w{5,17}$",
        "email" : "^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$",
        "mobilePhone" : "^(13+\d{9})|(159+\d{8})|(153+\d{8})$",
        "ch" : "[\u4e00-\u9fa5]"
    };
    if (opts["customType"]) {
        Reg[opts["customType"]] = opts["customReg"];
        opts[opts["customType"]] = opts["customTip"];
        //需要放在formCheck-radioBox之前
        dataType.splice((dataType.length - 2), 1, ("formCheck-" + opts["customType"]));
    }
    var This = this;
    for (var i = 0; i < this.nodes.length; i++) {
        var form = this.nodes[i];
        form.nodes = []; //dataType匹配的表单元素集合的超集,保存在每个form下
        for (var j = 0; j < dataType.length; j++) { //表单元素超集_二维数组;
            var resultArr = this.getClass(dataType[j], form);
            if (resultArr.length != 0) {
                resultArr.dataClass = dataType[j]; //将对应class值绑定在子数组上
                form.nodes.push(resultArr);
            }
        }
        for (var k = 0; k < form.nodes.length; k++) { //绑定blur事件
            (function () {
                if (form.nodes[k].dataClass != "formCheck-radioBox") {
                    var regoptsKEY = form.nodes[k].dataClass.slice(form.nodes[k].dataClass.indexOf("-") + 1);
                    var regTest = new RegExp(Reg[regoptsKEY]);
                    for (var l = 0; l < form.nodes[k].length; l++) {
                        form.nodes[k][l].onblur = function () {
                            var wrongSpan = This.getClass("formCheck-wrong", this.parentNode)[0];
                            if (!regTest.test(this.value)) {
                                wrongSpan.innerHTML = opts[regoptsKEY];
                                wrongSpan.style.cssText = def["wrongStyle"];
                            } else {
                                wrongSpan.innerHTML = def["passContent"];
                                wrongSpan.style.cssText = def["passStyle"];
                            }
                        }
                    }
                } else {
                    if (form.nodes[k].dataClass == "formCheck-radioBox") {
                        for (var m = 0; m < form.nodes[k].length; m++) {
                            var RBA = form.nodes[k][m];
                            (function (RBA) {
                                //每组radio或checkBox的input集合,form.nodes[k][m].radioBoxArr三维数组
                                form.nodes[k][m].radioBoxArr = form.nodes[k][m].parentNode.getElementsByTagName("input");
                                for (var n = 0; n < form.nodes[k][m].radioBoxArr.length; n++) {
                                    if (form.nodes[k][m].radioBoxArr[n].checked) {
                                        var statePre = true;
                                        break;
                                    } else {
                                        var statePre = false;
                                    }
                                    form.nodes[k][m].state = statePre;
                                    form.nodes[k][m].radioBoxArr[n].onclick = function () {
                                        for (var n = 0; n < RBA.radioBoxArr.length; n++) {
                                            if (RBA.radioBoxArr[n].checked) {
                                                var statePre = true;
                                                break;
                                            } else {
                                                var statePre = false;
                                            }
                                        }
                                        RBA.state = statePre;
                                        var wrongSpan = This.getClass("formCheck-wrong", this.parentNode)[0];
                                        if (RBA.state) {
                                            wrongSpan.innerHTML = opts["passContent"];
                                            wrongSpan.style.cssText = opts["passStyle"];
                                        } else {
                                            wrongSpan.innerHTML = opts["radioBox"];
                                            wrongSpan.style.cssText = opts["wrongStyle"];
                                        }
                                    }
                                }
                            })(RBA)
                        }
                    }
                }
            })()
        }
        (function (form) {
            form.onsubmit = function (e) {
                var e = e || window.event;
                for (var k = 0; k < form.nodes.length; k++) {
                    var regoptsKEY = form.nodes[k].dataClass.slice(form.nodes[k].dataClass.indexOf("-") + 1);
                    var regTest = new RegExp(Reg[regoptsKEY]);
                    for (var l = 0; l < form.nodes[k].length; l++) {
                        if (def["submitCheck"]) {
                            if (form.nodes[k].dataClass != "formCheck-radioBox") {
                                var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0];
                                if (!regTest.test(form.nodes[k][l].value)) {
                                    wrongSpan.innerHTML = opts[regoptsKEY];
                                    wrongSpan.style.cssText = def["wrongStyle"];
                                    return false; //停止执行中断循环,阻止默认
                                } else {
                                    wrongSpan.innerHTML = def["passContent"];
                                    wrongSpan.style.cssText = def["passStyle"];
                                }
                            } else if (form.nodes[k].dataClass == "formCheck-radioBox") {
                                var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0];
                                if (form.nodes[k][l].state) {
                                    wrongSpan.innerHTML = opts["passContent"];
                                    wrongSpan.style.cssText = opts["passStyle"];
                                } else {
                                    wrongSpan.innerHTML = opts["radioBox"];
                                    wrongSpan.style.cssText = opts["wrongStyle"];
                                    return false;
                                }
                            }
                        } else {
                            if (form.nodes[k].dataClass != "formCheck-radioBox") {
                                var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0];
                                if (!regTest.test(form.nodes[k][l].value)) {
                                    wrongSpan.innerHTML = opts[regoptsKEY];
                                    wrongSpan.style.cssText = opts["wrongStyle"];
                                    e.preventDefault();
                                    e.returnValue = false;
                                } else {
                                    wrongSpan.innerHTML = opts["passContent"];
                                    wrongSpan.style.cssText = opts["passStyle"];
                                }
                            } else if (form.nodes[k].dataClass == "formCheck-radioBox") {
                                var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0];
                                if (form.nodes[k][l].state) {
                                    wrongSpan.innerHTML = opts["passContent"];
                                    wrongSpan.style.cssText = opts["passStyle"];
                                } else {
                                    wrongSpan.innerHTML = opts["radioBox"];
                                    wrongSpan.style.cssText = opts["wrongStyle"];
                                    e.preventDefault();
                                    e.returnValue = false;
                                }
                            }
                        }
                    }
                }
            }
        })(form)
    }
}
Copier après la connexion
H(".wrap").page(opts); le conteneur de page passe dans le paramètre page requis pour instancier le contrôle de pagination :

*Nombre total de pages pageSize = nombre total d'éléments de données dans totalData ÷ nombre d'éléments de données dans chaque page de pageItems (le quotient est arrondi au supérieur). Les données de configuration nécessaires sont renvoyées par le serveur lors de l'initialisation.

*pageSize<6 Le bouton de numéro de page est généré en fonction de pageSize, forçant pageBtns à être égal à pageSize pour générer des numéros de page statiques.

*6≤pageSize<10 Le numéro de page est généré en fonction de nowPage, supprime de force les boutons de page haut et bas, définit pageBtns=6 et distingue trois situations de nowPage au début, à la fin et au milieu.

*pageSize≥10 Le numéro de page est généré en fonction de nowPage. En considérant s'il y a des pages supérieures et inférieures, nowPage est au début, à la fin et au milieu des situations 2*3=6.

*页码序数基于等差数列通项公式【An=a1+(n-1)*d】推导并输出在按钮data-page上,a1由pageBtns、pageSize确定。

*上下页、首尾页、当前页、禁用页、省略页按钮根据不同情况重写data-page和内容,并判断启用或者禁用,对比nowPage报告当前页等。

*监听容器点击操作修改nowPage并更新所有页码,回调函数opts["cb"]接收nowPage用于数据请求,需过滤无效页码按钮避免无效更新浪费性能。

*本控件充分利用事件委托,文档碎片createDocumentFragment()原理优化批量节点的事件处理和DOM操作,节约性能损耗。

Heng.prototype.page = function (opts) {
    /*opts={"totalData":总数据条数(后台取),"pageItems":每页数据条数(后台取),"pageSize":总页数","pageBtns":所有按钮总数(含省略号上下页),
    "preNext":有无上下页,"nowPage":当前页码,"active":激活样式class,"disable",禁用样式class,"headText":"首页","endText":"尾页","cb":回调函数}
    "preText":上一页按钮文本,"nextText":"下一页按钮文本"*/
    /*总页数=总数据条数÷每页数据条数并向上取整 && pageSize>=6基于nowPage生成页码时有上下页pageBtns>=10最佳 && 无上下页pageBtns>= 6最佳 && pageSize<6时基于pageSize生成页码强制pageSize==pageBtns*/
    var def = {
        "pageSize" : Math.ceil(opts["totalData"] / opts["pageItems"]),
        "preNext" : true,
        "preText" : "上一页",
        "nextText" : "下一页",
        "nowPage" : 1,
        "headText" : "1",
        "endText" : opts["pageSize"],
        "pageBtns" : 10
    };
    opts = H.extend(def, opts);
    var isHead,
    isEnd,
    isHeadPN,
    isEndPN,
    frag = document.createDocumentFragment();
    for (var i = 0; i < this.nodes.length; i++) {
        var wrap = this.nodes[i];
        int();
        wrap.onclick = function (e) { //点击更新页码,确定当前页,执行回调函数
            var e = e || window.event;
            var target = e.target || e.srcElement;
            if (target.getAttribute("data-page") && target.getAttribute("data-page") != opts["nowPage"]) {
                //全部页码基于nowPage及其所在位置生成,更新nowPage才能更新页码
                opts["nowPage"] = parseInt(target.getAttribute("data-page"));
                int();
            } else if (target.getAttribute("data-page") && target.getAttribute("data-page") == opts["nowPage"]) {
                opts["cb"] && opts["cb"](opts["nowPage"]);
            } else if (!target.getAttribute("data-page")) {
                return false
            }
        }
        wrap.onselectstart = function () {
            return false
        }
    }
    function int() {
        wrap.innerHTML && (wrap.innerHTML = null);
        if (opts["pageSize"] < 10 && opts["pageSize"] >= 6) {
            opts["preNext"] = false;
            opts["pageBtns"] = 6;
        } else if (opts["pageSize"] < 6) {
            static();
            opts["pageSize"];
        }
        if (opts["preNext"]) { //循环外判断一次即可
            if (opts["pageBtns"] % 2 == 1) {
                isHeadPN = opts["nowPage"] <= (opts["pageBtns"] + 1) / 2;
                isEndPN = opts["nowPage"] >= opts["pageSize"] + 1 - (opts["pageBtns"] - 1) / 2;
            }
            if (opts["pageBtns"] % 2 == 0) {
                isHeadPN = opts["nowPage"] <= opts["pageBtns"] / 2;
                isEndPN = opts["nowPage"] >= [opts["pageSize"] + 1 - (opts["pageBtns"] / 2)]
            }
        } else {
            if (opts["pageBtns"] % 2 == 1) {
                isHead = opts["nowPage"] <= (opts["pageBtns"] + 1) / 2;
                isEnd = opts["nowPage"] >= [opts["pageSize"] - (opts["pageBtns"] - 1) / 2];
            }
            if (opts["pageBtns"] % 2 == 0) {
                isHead = opts["nowPage"] <= (opts["pageBtns"] / 2);
                isEnd = opts["nowPage"] >= [opts["pageSize"] - opts["pageBtns"] / 2]
            }
        }
        if (opts["pageSize"] >= 6) {
            for (var j = 1; j <= opts["pageBtns"]; j++) {
                var oSp = document.createElement("span");
                if (opts["preNext"] && (opts["pageSize"] >= 10)) {
                    if (opts["pageBtns"] < 10) {
                        opts["pageBtns"] = 10;
                    };
                    if (isHeadPN) { //nowPage靠近头部
                        oSp.innerHTML = j - 1;
                        oSp.setAttribute("data-page", j - 1);
                        if (j == opts["pageBtns"] - 2) {
                            oSp.innerHTML = "…"; //重写倒数第三项
                            oSp.removeAttribute("data-page");
                        }
                    } else if (isEndPN) { //nowPage靠近尾部
                        oSp.innerHTML = opts["pageSize"] - opts["pageBtns"] + j + 1;
                        oSp.setAttribute("data-page", opts["pageSize"] - opts["pageBtns"] + j + 1)
                        if (j == 3) {
                            oSp.innerHTML = "…"; //重写第三项
                            oSp.removeAttribute("data-page");
                        }
                    } else { //nowPage中间,页码基于nowPage生成
                        middle();
                        if (j == opts["pageBtns"] - 2 || j == 3) {
                            oSp.innerHTML = "…"; //重写第三项和倒数第三项
                            oSp.removeAttribute("data-page");
                        }
                    }
                    //重写所有的第一项和倒数第一项
                    if (j == 1) {
                        oSp.innerHTML = opts["preText"];
                        if (opts["nowPage"] == 1) {
                            oSp.setAttribute("class", opts["disable"]);
                            oSp.removeAttribute("data-page");
                        } else {
                            oSp.setAttribute("data-page", opts["nowPage"] - 1);
                        }
                    } else if (j == opts["pageBtns"]) {
                        oSp.innerHTML = opts["nextText"];
                        if (opts["nowPage"] == opts["pageSize"]) {
                            oSp.setAttribute("class", opts["disable"]);
                            oSp.removeAttribute("data-page");
                        } else {
                            oSp.setAttribute("data-page", opts["nowPage"] + 1);
                        }
                    }
                    //重写所有的第二项和倒数第二项
                    if (j == 2) {
                        oSp.innerHTML = opts["headText"];
                        oSp.setAttribute("data-page", 1)
                    } else if (j == opts["pageBtns"] - 1) {
                        oSp.innerHTML = opts["endText"];
                        oSp.setAttribute("data-page", opts["pageSize"])
                    }
                } else if (!opts["preNext"] && (opts["pageSize"] >= 6)) {
                    if (opts["pageBtns"] < 6) {
                        opts["pageBtns"] = 6
                    };
                    if (isHead) {
                        oSp.innerHTML = j;
                        oSp.setAttribute("data-page", j);
                        if (j == opts["pageBtns"] - 1) {
                            oSp.innerHTML = "…";
                            oSp.removeAttribute("data-page");
                        }
                    } else if (isEnd) {
                        oSp.innerHTML = opts["pageSize"] - opts["pageBtns"] + j;
                        oSp.setAttribute("data-page", opts["pageSize"] - opts["pageBtns"] + j)
                        if (j == 2) {
                            oSp.innerHTML = "…";
                            oSp.removeAttribute("data-page");
                        }
                    } else {
                        middle();
                        if (j == opts["pageBtns"] - 1 || j == 2) {
                            oSp.innerHTML = "…";
                            oSp.removeAttribute("data-page");
                        }
                    }
                    //重写第一项和倒数第一项
                    if (j == 1) {
                        oSp.innerHTML = opts["headText"];
                        oSp.setAttribute("data-page", 1)
                    } else if (j == opts["pageBtns"]) {
                        oSp.innerHTML = opts["endText"];
                        oSp.setAttribute("data-page", opts["pageSize"])
                    }
                }
                if (oSp.getAttribute("data-page") === opts["nowPage"].toString()) {
                    oSp.setAttribute("class", opts["active"]);
                }
                oSp.style.cursor = "pointer";
                (!oSp.getAttribute("data-page")) && (oSp.style.cursor = "auto");
                frag.appendChild(oSp);
            } //for
            wrap.appendChild(frag);
        }
        function middle() {
            if (opts["pageBtns"] % 2 == 1) {
                oSp.innerHTML = opts["nowPage"] - [(opts["pageBtns"] + 1) / 2] + j;
                oSp.setAttribute("data-page", opts["nowPage"] - [(opts["pageBtns"] + 1) / 2] + j)
            } else if (opts["pageBtns"] % 2 == 0) {
                oSp.innerHTML = opts["nowPage"] - (opts["pageBtns"] / 2) + j;
                oSp.setAttribute("data-page", opts["nowPage"] - (opts["pageBtns"] / 2) + j)
            }
        }
        function static() {
            for (var k = 1; k <= opts["pageSize"]; k++) {
                var oSp = document.createElement("span");
                oSp.innerHTML = k;
                oSp.setAttribute("data-page", k);
                frag.appendChild(oSp);
                if (oSp.getAttribute("data-page") === opts["nowPage"].toString()) {
                    oSp.setAttribute("class", opts["active"]);
                }
            }
            wrap.appendChild(frag);
        }
        opts["cb"] && opts["cb"](opts["nowPage"]) //报告当前页发送请求用
    } //int
}
Copier après la connexion

*

H("p").on ("Tap",fn/{"Tap":fn,"Hold":fn});自定义事件处理程序实现触控手势操作:

*触控手势不属于系统事件,需一套在数据层管理事件及函数添加、销毁、遍历执行的机制,模拟系统原生add/removeEventListener方法的功能。

*触控手势事件的触发依赖系统touchstart/touchmove/touchend,触点ID号identifier跟踪同一手指。通过e.data报告手势状态。

*Tap:屏幕停留时间小于250ms,因不可避免手指抖动、力度不均影响触点状态,手指离屏时坐标偏移需小于阀值(水平:10px,垂直:5px)。

*Pinch:手指移动中水平或垂直方向坐标相对进屏时偏移大于10px时可触发,两根手指水平垂直偏移量4个值中的最大者作为缩放系数e.data.k。

*Hold:手指在屏幕停留时间大于500ms,如果提前离开或者手指偏移量大于阀值(水平:10px,垂直:5px)则停止定时器不触发。

*swipe:手指需在1000ms内连续(中途不反向)移动至少30px,对比进出屏触点坐标水平垂直偏移量,取较大者来确定e.data报告滑动方向和距离。

/*自定义事件处理程序*/
H.addEvent = function (type, handler) {
    this.handlers = {}; //{type1:[fn],type2:[fn]}
    if (typeof this.handlers[type] == "undefined") {
        this.handlers[type] = [];
    }
    this.handlers[type].push(handler);
};
H.fireEvent = function (type, data) {//调用对应类型函数触发事件
    if (this.handlers[type]instanceof Array) {
        var arrayEvent = this.handlers[type];
        for (var i = 0, len = arrayEvent.length; i < len; i++) {
            if (typeof arrayEvent[i] === "function") {
                arrayEvent[i](data);
            }
        }
    }
};
H.removeEvent = function (type, handler) {
    if (this.handlers[type]instanceof Array) {
        var arrayEvent = this.handlers[type];
        for (var i = 0, len = arrayEvent.length; i < len; i++) {
            if (arrayEvent[i] === handler) {
                arrayEvent.splice(i, 1);
                break;
            }
        }
    }
}
/*触控手势实现和事件绑定*/
Heng.prototype.on = function (handle, fn) { //调用方式(type,fn) 或 ({type1:fn1,type2:fn2})
    for (var i = 0; i < this.nodes.length; i++) {
        var node = this.nodes[i];
        var iStouch = false;
        var hasTap = false,
        hasPinch = false,
        hasHold = false,
        hasSwipeLeft = false,
        hasSwipeRight = false;
        left = true,
        right = true,
        up = true,
        down = true;
        var inTime,
        outTime,
        touchID,
        touchID1,
        inX,
        inY,
        inX1,
        inY1,
        outX,
        outY,
        moveX,
        moveY,
        moveX1,
        moveY1,
        disX,
        disY,
        disX1,
        disY1,
        t;
        if (arguments.length == 1) {
            for (var k in handle) {
                if ((k === "Tap") || (k === "Pinch") || (k === "Hold") || (k === "Swipe")) {
                    iStouch = true; //触控和鼠标事件不和共用
                    this.iStouch = iStouch;
                    checkType(k);
                }
            }
        } else {
            iStouch = (handle === "Tap") || (handle === "Pinch") || (handle === "Hold") || (handle === "Swipe");
            this.iStouch = iStouch;
            checkType(handle);
        }
        if (iStouch) {
            H.bind(node, "touchstart", tsFn);
            H.bind(node, "touchend", teFn);
            if (!hasTap) {
                H.bind(node, "touchmove", tmFn);
            }
            if (arguments.length == 1) {
                for (var j in handle) {
                    H.addEvent(j, handle[j]);
                }
            } else {
                H.addEvent(handle, fn);
            }
        } else {
            if (arguments.length == 1) {
                for (var j in handle) {
                    H.bind(node, j, handle[j]);
                }
            } else {
                H.bind(node, handle, fn)
            }
        }
    }
    function checkType(x) {
        switch (x) {
        case "Tap":
            hasTap = true;
            break;
        case "Pinch":
            hasPinch = true;
            break;
        case "Hold":
            hasHold = true;
            break;
        case "swipeLeft":
            hasSwipe = true;
            break;
        }
    }
    function tsFn(e) {
        touchID = e.changedTouches[0].identifier;
        inTime = new Date().getTime();
        inX = e.changedTouches[0].clientX;
        inY = e.changedTouches[0].clientY;
        if (e.changedTouches[1]) {
            touchID1 = e.changedTouches[1].identifier;
            inX1 = e.changedTouches[1].clientX;
            inY1 = e.changedTouches[1].clientY;
        }
        if (hasHold) {
            if (e.targetTouches.length === 1 && e.changedTouches[0].identifier === touchID) {
                t = window.setTimeout(function () {
                        H.fireEvent("Hold", e);
                    }, 500)
            }
        }
    }
    function tmFn(e) {
        if (hasHold) {
            if ([Math.abs(moveY - inY) >= 5] && [Math.abs(moveX - inX) >= 10]) {
                window.clearTimeout(t);
            }
        } else if (hasPinch && e.targetTouches.length === 2 && e.changedTouches[1].identifier === touchID1 && e.changedTouches[0].identifier === touchID) {
            e.preventDefault();
            disX = Math.abs(inX1 - inX);
            disY = Math.abs(inY1 - inY);
            disX1 = Math.abs(moveX1 - moveX);
            disY1 = Math.abs(moveY1 - moveY);
            if ((Math.abs(disX - disX1) >= 10) || (Math.abs(disY - disY1) >= 10)) {
                e.data.k = (Math.abs(disX - disX1) > Math.abs(disY - disY1)) ? (disX1 / disX) : (disY1 / disY); //缩放因子
                H.fireEvent("Pinch", e);
            }
        } else if (hasSwipe && e.targetTouches.length === 2) {
            if (e.changedTouches[0].clientX >= moveX) {
                left = true
            } //对比相邻两次的移动判断是否中途反向
            else {
                right = false
            }
            if (e.changedTouches[0].clientY >= moveY) {
                up = true
            } else {
                down = false
            }
            e.preventDefault();
        }
        moveX = e.changedTouches[0].clientX;
        moveY = e.changedTouches[0].clientY;
        if (e.changedTouches[1]) {
            moveX1 = e.changedTouches[1].clientX;
            moveY1 = e.changedTouches[1].clientY;
        }
    }
    function teFn(e) {
        outTime = new Date().getTime();
        outX = e.changedTouches[0].clientX;
        outY = e.changedTouches[0].clientY;
        if (hasTap && e.targetTouches.length === 1 && e.changedTouches[0].identifier === touchID) {
            if ([(outTime - inTime) <= 250] && [Math.abs(outY - inY) <= 5] && [Math.abs(outX - inX) <= 10]) {
                H.fireEvent("Tap", e);
            }
        } else if (hasHold && (outTime - inTime) <= 500) {
            window.clearTimeout(t);
        } else if (hasSwipe && e.targetTouches.length === 1 && e.changedTouches[0].identifier === touchID) {
            if (Math.abs(outX - inX) >= Math.abs(outY - inY)) {
                e.data.dis = Math.abs(outX - inX);
                if (outX >= inX) {
                    e.data.direction = "right"
                } else {
                    e.data.direction = "left"
                }
            } else {
                e.data.dis = Math.abs(outY - inY);
                if (outY >= inY) {
                    e.data.direction = "down"
                } else {
                    e.data.direction = "up"
                }
            }
            if ((outTime - inTime) <= 1000 && (Math.abs(outY - inY) >= 30 || Math.abs(outX - inX) >= 30)) {
                if ((left && right) || (up && down)) { //保证中途连续不反向
                    H.fireEvent("swipe", e);
                }
            }
        }
    }
    return this;
};
Copier après la connexion

*

*【静态方法案例】

H.ajaxJsonp(opts);数据请求包含ajax和jsonp两大模块:

*jsonp:基于src跨域特性动态插入script标签发送get请求远程调用本地同名函数opts["jsonpCBname"],数据以函数传参为载体传回本地。

*发送数据opts['data']自动序列化(支持Array、Object、String等类型);通过success获取ajax或jsonp请求结果并自动解析。

*post必须定义Content-Type类型,默认setRequestHeader("Content-Type","application/x-www-form-urlencoded)。

*区分同步异步请求过程,并处理了success、error、progress、complete等四个状态事件。

*若opts['dataType']=jsonp将切换为jsonp 请求,其它值为ajax,opts['data']自动拼接到get请求地址或推入send()后post数据。

*jsonp回调函数名默认随机生成并自动发送,执行回调后立即清除本次生成的script标签防止无限插入,同时清空回调释放内存。

/*ajax and jsonp*/
H.ajaxJsonp = function (opts) {
    /*opts={"method":"GET/POST","url":地址,"async":布尔,"data":数组对象(内部序列化)或已序列化的字符串,"cache":是否禁用缓存,
    "dataType":期望返回数据类型(如果取值jsonp将进行JSONP请求),"jsonp":后台获取回调函数名的键名默认callback,
    "jsonpCBname":回调函数名默认随机生成,"time":超时,"contentType":发送文件类型,
    "success":fn(response)与jsonp请求共用回调函数,"error":fn(status, statusText),"progress":fn,"complete":fn}*/
    //返回结果已内部解析
    var def = {
        "method" : "POST",
        "async" : true,
        "data" : null,
        "cache" : true,
        "time" : 800,
        "dataType" : "text",
        "contentType" : "application/x-www-form-urlencoded",
        "jsonp" : "callback"
    };
    for (var p in opts) {
        def[p] = opts[p];
    };
    opts = def;
    var dataStr = "";
    if (opts[&#39;data&#39;]) {
        dataType = Object.prototype.toString.call(opts[&#39;data&#39;]).toLowerCase();
        if (dataType == "[object array]") {
            for (var i = 0; i < opts[&#39;data&#39;].length; i++) {
                opts[&#39;data&#39;][i] = "" + i + "=" + encodeURIComponent(opts[&#39;data&#39;][i]);
            }
            dataStr = opts[&#39;data&#39;].join("&");
        } else if (dataType == "[object object]") {
            for (x in opts[&#39;data&#39;]) {
                dataStr += "&" + x + "=" + encodeURIComponent(opts[&#39;data&#39;][x]);
            }
        } else if (dataType == "[object string]") {
            /*需为序列化字符串*/
            dataStr = opts[&#39;data&#39;];
        }
    }
    if (opts[&#39;dataType&#39;] == &#39;jsonp&#39;) { //jsonp请求
        var a = Math.floor(Math.random() * 26) + "a".charCodeAt(0);
        var b = Math.floor(Math.random() * 26) + "a".charCodeAt(0);
        var c = Math.floor(Math.random() * 26) + "a".charCodeAt(0);
        var d = Math.floor(Math.random() * 26) + "a".charCodeAt(0);
        opts["jsonpCBname"] = opts["jsonpCBname"] || String.fromCharCode(a, b, c, d);
        if (opts[&#39;url&#39;].indexOf(&#39;?&#39;) === -1) {
            opts[&#39;url&#39;] += &#39;?&#39; + opts[&#39;jsonp&#39;] + &#39;=&#39; + opts[&#39;jsonpCBname&#39;] + &#39;&&#39; + dataStr;
        } else {
            opts[&#39;url&#39;] += &#39;&&#39; + opts[&#39;jsonp&#39;] + &#39;=&#39; + opts[&#39;jsonpCBname&#39;] + &#39;&&#39; + dataStr;
        }
        if (opts[&#39;cache&#39;]) {
            opts[&#39;url&#39;] += &#39;&cache=&#39; + Date.now();
        }
        var script = document.createElement(&#39;script&#39;);
        script.id = "" + opts["jsonpCBname"];
        script.src = opts[&#39;url&#39;];
        document.getElementsByTagName(&#39;head&#39;)[0].appendChild(script);
        window[opts["jsonpCBname"]] = function (response) { //服务器调用的函数
            opts[&#39;success&#39;] && opts[&#39;success&#39;](response);
            document.getElementById(opts["jsonpCBname"]).parentNode.removeChild(document.getElementById(opts["jsonpCBname"]));
            window[opts["jsonpCBname"]] = null;
        }

    } else { //Ajax请求
        var xhr = new XMLHttpRequest();
        if (opts.method === &#39;get&#39;) {
            opts.url += opts.url.indexOf(&#39;?&#39;) == -1 ? &#39;?&#39; + dataStr : &#39;&&#39; + dataStr;
        }
        if (opts.async === true) {
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 0) {
                    opts.progress();
                } else if (xhr.readyState == 4) {
                    callback();
                    opts.complete();
                }
            };
        }
        xhr.open(opts.method, opts.url, opts.async);
        if (opts["cache"]) {
            xhr.setRequestHeader("Cache-Control", "no-cache");
            xhr.setRequestHeader("If-Modified-Since", "0");
        }
        xhr.responseType = opts["dataType"];
        xhr.timeout && (xhr.timeout = opts["time"])
        if (opts.method === &#39;post&#39;) { //实际发送文件类型POST必须
            xhr.setRequestHeader("Content-Type", opts["contentType"]);
            xhr.send(dataStr);
        } else {
            xhr.send(null);
        }
        if (opts.async === false) {
            callback();
            opts.complete();
        }
        function callback() {
            var response;
            if (xhr.status == 200) {
                try {
                    response = JSON.parse(xhr.responseText);
                } catch (er) {
                    response = eval("(" + xhr.responseText + ")");
                }
                opts.success(response);
            } else {
                opts.error(xhr.status, xhr.statusText);
            }
        }
    } //ajax End
}
Copier après la connexion

相关推荐:

前端框架ThinkJS框架详解

layui前端框架日期控件使用方法

最新的前端框架、类库、工具比较

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!