Detailed explanation of front-end framework Heng.js

小云云
Release: 2023-03-21 08:16:02
Original
3682 people have browsed it

Framework description: Based on object-oriented programming ideas and implemented in native js language, it does not rely on any third-party js library, and does not reference any external code fragments! Fully consider different application scenarios to ensure the flexibility and robustness of the framework.
* Implementation function: It implements the architecture and internal details that are completely consistent with jQuery. The internal loop of the setting operation can set all matching nodes, and the method can be called in a chain.
* Method classification: Static methods are used within the framework for data logic, DOM preprocessing and other global non-DOM operations; instance methods call corresponding UI components.
* Parameter configuration: Configure different parameters according to actual needs and call them flexibly. If there are no parameters, use the default configuration.
* Selector supports: id, class, label, descendant selector, dom node, instantiated object H ("css selector").
* Compatibility: All methods are fully cross-browser compatible and compatible with most current browsers (IE6/7/8/9/10/11, chrome, firefox, Safari, etc.).
* Variable safety: In a closure environment, ensure the safety of the framework's own variables and avoid variable conflicts
* Framework performance: Optimize the code to improve performance and minimize browser rearrangements and redraws. Release memory by assigning a null value; avoid island communication between DOM, BOM, and ECMAScript; perform DOM operations based on document fragments; bubbling delegates to process collection events...
*
* [Call]
* Static method: H.Method(value)
* Instance method: H("css selector").Method(value), H("css selector").Method({key:value})
* H(sel).Method({property event configuration}), H(sel).Method(component method, method parameter)
* Chain call: H("css selector").Method({key :value}).Method()
* Execution after the DOM tree is loaded: H(fn) is equivalent to $(fn)===$(document).ready(fn)
in the jQuery library * Internal Loop: The instance method performs an internal loop on the set of nodes matched by H ("css selector"), and can operate all matching nodes at the same time
* Attribute method: opts sets the component attribute event; component method Method (component method, parameter) ;
* Gesture touch: Encapsulates touch gesture events (Tap, Pinch, Hold, Swipe) for the mobile terminal, and reports the status of the corresponding gesture through e.data. Such as pinch zoom coefficient, sliding direction, sliding distance, etc.
*
* Author: wheng

* Date: 2015-07-25
*/

* Framework source code http://whwheng.gitee. io/csdn

*

*[Brief description of structure]

* The main body of the framework ensures its own safety in the closure environment and avoids variable conflicts; outward through H or Heng Reference base class.

(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)
Copy after login

*The constructor implements the "css selector" function to obtain DOM nodes and store the nodes on the nodes attribute, following the rules of "public data attribution".

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;
        }
    }
}
Copy after login

*Static methods do not require instantiation and are used for data logic and DOM preprocessing or other global non-DOM operations within the framework; instance methods are combined with DOM nodes to complete the requirements. If the sel matching component entry is a collection, Then all nodes can implement the logic and functions of the method.

//静态方法
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;);
        ……
    }
}
Copy after login

*Object-oriented programming will inevitably lead to confusion of this pointer. For all similar problems, declare var This=this outside the function through closure; correct this pointer.

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);//数据合并
    ……
}
Copy after login

*

*[Instance method case]

H("form").formCheck(); One line of code completes all form verification on the page, and the functions are as follows:

*All form elements of any type within the form matched by H("form") will be verified.

*Each form element will be independently verified when it loses focus.

*When the form is submitted, all internal elements are uniformly verified, divided into "one-by-one verification (interrupting subsequent checks when encountering elements that fail verification)" and "one-time verification (checking all elements at once)" Two modes, switch between the two modes by configuring opts["submitCheck"]=true/false.

*Whether the verification passes or not, there will be a prompt message indicating the success or failure of the corresponding verification type. You can configure the opts parameter to customize the content and style information of the prompt.

*Built-in verification of common data types, and the verification type can also be extended through opts["customType"] and opts["customReg"], which greatly improves the flexibility of form verification.

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)
    }
}
Copy after login

*

H(".wrap").page(opts); The page container passes in the required parameter page to instantiate the paging control:

*Total Number of pages pageSize = total number of data items in totalData ÷ number of data items per page in pageItems (the quotient is rounded up). Necessary configuration data is returned by the server during initialization.

*pageSize<6 page number button is generated based on pageSize, forcing pageBtns to be equal to pageSize, and generating static page numbers.

*6≤pageSize<10 The page number is generated based on nowPage, forcibly delete the up and down page buttons, define pageBtns=6, and distinguish three situations of nowPage at the beginning, end and middle.

*pageSize≥10 The page number is generated based on nowPage. Considering whether there are upper and lower pages, nowPage is at the beginning, end and middle 2*3=6 situations.

*页码序数基于等差数列通项公式【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
}
Copy after login

*

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;
};
Copy after login

*

*【静态方法案例】

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
}
Copy after login

相关推荐:

前端框架ThinkJS框架详解

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

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

The above is the detailed content of Detailed explanation of front-end framework Heng.js. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!