>백엔드 개발 >PHP 튜토리얼 >프론트엔드 프레임워크 Heng.js에 대한 자세한 설명

프론트엔드 프레임워크 Heng.js에 대한 자세한 설명

小云云
小云云원래의
2018-03-10 14:58:463725검색

프레임워크 설명: 객체 지향 프로그래밍 아이디어를 기반으로 하고 기본 js 언어로 구현되어 타사 js 라이브러리에 의존하지 않으며 외부 코드 조각을 참조하지 않습니다! 프레임워크의 유연성과 견고성을 보장하기 위해 다양한 애플리케이션 시나리오를 완전히 고려하십시오.
* 구현 기능: jQuery와 완전히 일치하는 아키텍처 및 내부 세부 사항을 구현합니다. 설정 작업의 내부 루프는 일치하는 모든 노드를 설정할 수 있으며 메서드는 체인에서 호출될 수 있습니다.
* 메소드 분류: 정적 메소드는 데이터 로직, DOM 전처리 및 기타 전역 비 DOM 작업을 위해 프레임워크 내에서 사용되며 해당 UI 구성요소를 호출합니다.
* 매개변수 구성: 실제 필요에 따라 다양한 매개변수를 구성하고 유연하게 호출합니다. 매개변수가 없는 경우 기본 구성을 사용합니다.
* 선택기 지원: ID, 클래스, 라벨, 하위 선택기, dom 노드, 인스턴스화된 객체 H("css 선택기").
* 호환성: 모든 방법은 브라우저 간 완벽하게 호환되며 대부분의 최신 브라우저(IE6/7/8/9/10/11, Chrome, Firefox, Safari 등)와 호환됩니다.
* 변수 안전성: 클로저 환경에서 프레임워크 자체 변수의 안전성이 보장되고 변수 충돌이 방지됩니다.
* 프레임워크 성능: 코드를 최적화하여 성능을 향상시키고 브라우저 리플로우 및 다시 그리기를 최소화합니다. null 값을 할당하여 메모리를 해제합니다. DOM, BOM 및 ECMAScript 간의 아일랜드 통신을 방지합니다. 문서 조각을 기반으로 DOM 작업을 수행하여 컬렉션 이벤트를 처리합니다...
*
* [호출]
* 정적 메서드: H. Method(value)
* 인스턴스 메소드: H("css selector").Method(value), H("css selector").Method({key:value})
* H(sel).Method({ 속성 이벤트 구성}), H(sel).Method(컴포넌트 메소드, 메소드 매개변수)
* 체인 호출: H("css selector").Method({key:value}).Method()
* DOM 트리 로드 후 실행: H(fn)은 jQuery 라이브러리의 $(fn)===$(document).ready(fn)
과 동일합니다. * 내부 루프: 인스턴스 메소드는 내부적으로 H("css selector")와 일치합니다. 노드 컬렉션은 내부 루프를 수행합니다. 일치하는 모든 노드를 동시에 작동할 수 있습니다
* 속성 메소드: opts는 컴포넌트 속성 이벤트를 설정합니다. 컴포넌트 메소드 메소드(컴포넌트 메소드, 매개변수)
* 제스처 터치: 모바일 단말기에 대한 터치 제스처 이벤트(Tap)가 캡슐화됩니다. , Hold, Swipe)을 수행하고 e.data를 통해 해당 제스처의 상태를 보고합니다. 핀치 줌 계수, 슬라이딩 방향, 슬라이딩 거리 등
*
* 작성자: wheng

* 날짜: 2015-07-25
*/

* 프레임워크 소스코드 http://whwheng.gitee.io/csdn

*

*[구조적 간략한 설명]

* 프레임워크의 본체는 폐쇄 환경에서 자체 안전을 보장하고 다양한 충돌을 방지하며 H 또는 Heng을 통해 기본 클래스를 외부로 참조합니다.

(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)

*생성자에서는 "공용 데이터 귀속" 규칙에 따라 DOM 노드의 기능을 얻고 노드 속성에 노드를 저장하기 위해 "css 선택기"가 구현됩니다.

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;
        }
    }
}

*정적 메서드는 인스턴스화가 필요하지 않으며 프레임워크 내에서 데이터 논리 및 DOM 전처리 또는 기타 전역 비DOM 작업에 사용됩니다. sel 일치 구성 요소 항목이 있는 경우 인스턴스 메서드는 DOM 노드와 결합됩니다. 설정하면 모든 노드가 메소드의 논리와 기능을 실현할 수 있습니다.

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

*객체 지향 프로그래밍은 필연적으로 이 포인터에 대한 혼란을 야기합니다. 모든 유사한 문제의 경우 클로저를 통해 함수 외부에 var This=this를 선언하세요.

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

*

*[인스턴스 메소드 사례]

H("form").formCheck(); 한 줄의 코드로 페이지의 모든 폼 검증을 완료하며, 기능은 다음과 같습니다.

*H(" form") 일치 양식 내의 모든 유형의 모든 양식 요소가 확인됩니다.

*각 양식 요소는 포커스를 잃으면 독립적으로 유효성이 검사됩니다.

*양식 제출 시 내부의 모든 요소를 ​​균일하게 검증하며, '일대일 검증(검증에 실패한 요소 발견 시 후속 점검 중단)'과 '일회성 검증(모든 요소 확인)' 두 가지 모드로 구분됩니다. 즉시)". opts["submitCheck"]=true/false를 구성하여 두 모드 사이를 전환합니다.

*인증 통과 여부에 관계없이 해당 인증 유형의 성공 또는 실패를 나타내는 프롬프트 메시지가 표시됩니다. opts 매개변수를 구성하여 프롬프트의 내용과 스타일 정보를 맞춤 설정할 수 있습니다.

*일반 데이터 유형에 대한 검증 기능이 내장되어 있으며 opts["customType"] 및 opts["customReg"]를 통해 검증 유형을 확장할 수도 있어 양식 검증의 유연성이 크게 향상됩니다.

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)
    }
}

*

H(".wrap").page(opts); 페이지 컨테이너는 페이징 제어를 인스턴스화하기 위해 필수 매개변수 페이지를 전달합니다.

*총 페이지 수 pageSize=totalDataTotal 데이터 항목 수 ¶pageItems per 페이지 데이터 항목 수입니다(몫은 반올림됨). 필요한 구성 데이터는 초기화 중에 서버에서 반환됩니다.

*pageSize

*6≤pageSize

*pageSize≥10 nowPage를 기준으로 페이지 번호가 생성되며, 상위 페이지와 하위 페이지가 있는지 고려하면 nowPage는 시작, 끝, 중간 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
}

 *

 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;
};

 *

 *【静态方法案例】

 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
}

相关推荐:

前端框架ThinkJS框架详解

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

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

위 내용은 프론트엔드 프레임워크 Heng.js에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.