首页 > 后端开发 > php教程 > 前端框架Heng.js详解

前端框架Heng.js详解

小云云
发布: 2023-03-21 08:16:02
原创
3818 人浏览过

框架说明:基于面向对象编程思想以原生js语言实现,不依赖任何第三方js库, 未引用任何外部代码片段!充分考虑不同的应用场景保证框架的灵活与健壮。
 * 实现功能:实现了与jQuery完全一致的架构和内部细节,设置操作内部循环可设置所有匹配节点,方法可链式调用。
 * 方法分类:静态方法用于框架内部进行数据逻辑和DOM预处理以及其它全局非DOM操作;实例方法调用对应UI组件。
 * 参数配置:根据实际需求配置不同参数灵活调用,无参数时使用默认配置。
 * 选择器支持:id、class、标签、后代选择器、dom节点,实例化对象H("css选择器")。
 * 兼容性:所有方法做了跨浏览器全兼容处理,兼容当前绝大部分浏览器(IE6/7/8/9/10/11,chrome,firefox,Safari等)。
 * 变量安全:闭包环境中,保障框架自身安变量全,避免变量冲突
 * 框架性能:优化代码提升性能,尽量减少浏览器重排和重绘。如赋空值释放内存; 避免DOM,BOM,ECMAScript间孤岛通信; 基于文档碎片进行DOM操作; 冒泡委托处理集合事件......
 *
 * 【调用】
 * 静态方法: H.Method(value)
 * 实例方法: H("css选择器").Method(value), H("css选择器").Method({key:value})
 * H(sel).Method({属性事件配置}), H(sel).Method(组件方法,方法参数)
 * 链式调用: H("css选择器").Method({key:value}).Method()
 * DOM树加载完毕执行: H(fn) 等价于jQuery库中的$(fn)===$(document).ready(fn)
 * 内部循环:实例方法内部对H("css选择器")匹配的节点集合进行内部循环, 可同时操作所有匹配节点
 * 属性方法:opts设置组件属性事件;组件方法Method(组件方法,参数);
 * 手势触控:针对移动端封装了触控手势事件(Tap轻敲、Pinch捏合、Hold长按、Swipe滑动),通过e.data报告相应手势的状态。如捏合缩放系数,滑动方向,滑动距离等。
 *
 * Author: wheng

 * Date: 2015-07-25
 */

 * 框架源码  http://whwheng.gitee.io/csdn

 *

 *【结构简叙】

 * 框架主体在闭包环境中保证自身安全,避免变量冲突;通过H或Heng向外引用基础类。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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

登录后复制

*构造函数中实现“css选择器”获取DOM节点的功能并将节点存储到nodes属性上,遵循“公共数据属性化”的规则。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

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操作;实例方法结合DOM节点完成需求,如果sel匹配组件入口为一个集合,则所有节点都可以实现方法的逻辑与功能。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//静态方法

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

        ……

    }

}

登录后复制

*面向对象编程不可避免会出现this指向混乱的情况,所有类似问题通过闭包在函数外部声明var This=this;修正this指向。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

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"]扩展验证类型,极大的提高了表单验证的灵活性。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

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);页面容器传入必须参数page实例化即可实现分页控件:

*总页数pageSize=totalData总数据条数÷pageItems每页数据条数(商向上取整)。必要的配置数据在初始化时由服务器传回。

*pageSize<6页码按钮基于pageSize生成,强制pageBtns等于pageSize,生成静态页码。

*6≤pageSize<10页码基于nowPage生成,强制删除上下页按钮,定义pageBtns=6,区分nowPage在开始、结尾和中间3种情形。

*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操作,节约性能损耗。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

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报告滑动方向和距离。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

/*自定义事件处理程序*/

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标签防止无限插入,同时清空回调释放内存。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

/*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
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板