移动端纯原生JS不依赖ajax后台服务器实现省市县三级联动
最近好多天没有更新文章,是因为公司的项目忙的不行。今天有点时间,就突然想起在移动端项目中遇到三级联动的问题,网上查了很多资料,都是依赖各种插件,或者晦涩难于理解。于是,自己决定写一个出来。 当然,没有用到别的插件类库,也没有用ajax。写完这个小demo也学到不少,现在分享给大家代码。因为代码较多,我就不一个个解释了,源码里面添加了很多注释。 为了便于大家使用,我将html精简了许多。
结尾会有在线运行地址。
0 | <!DOCTYPE html> |
1 | <html lang="en"> |
2 | <head> |
3 | <meta charset="UTF-8"> |
4 | <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> |
5 | <meta name="apple-mobile-web-app-capable" content="yes"> |
6 | <title>三级联动</title> |
7 | <link rel="stylesheet" href="index.css"> |
8 | </head> |
9 | <body> |
10 | <div class="select"> |
11 | 请选择区域 |
12 | </div> |
13 | <script src="city.js"></script> |
14 | <script src="index.js"></script> |
15 | </body> |
16 | </html> |
至于三级联动的数据,我重新定义了一个js文件,在文件中创建了一个json对象,复制了服务器的三级联动的数据过来。 下面是javascript代码
0 | (function(){ |
1 | |
2 | var sel = document.querySelector('.select'), |
3 | createDivFlag = true; |
4 | windowHeight = window.screen.availHeight, |
5 | startYFlag = 0; |
6 | |
7 | var div,cur,province,city,county,ok,resut,oneNum,twoNum,threeNum,touchFlag, |
8 | touchOn = true, |
9 | touchStop = true, |
10 | tochBegin = true; |
11 | |
12 | //判断是否能打开 |
13 | sel.addEventListener('touchend',function(){ |
14 | |
15 | createDivFlag && createDiv(); |
16 | |
17 | }) |
18 | |
19 | //开始生成盒子 |
20 | function createDiv(){ |
21 | |
22 | //判断避免重复生成 |
23 | if(!div){ |
24 | div = document.createElement('div'), |
25 | cur = document.createElement('div'), |
26 | ok = document.createElement('div'), |
27 | resut = document.createElement('div'), |
28 | province = document.createElement('div'), |
29 | city = document.createElement('div'), |
30 | county = document.createElement('div'); |
31 | |
32 | div.className = 'win'; |
33 | cur.className = 'current'; |
34 | ok.className = 'ok'; |
35 | resut.className = 'resut'; |
36 | province.className = 'province'; |
37 | city.className = 'city'; |
38 | county.className = 'county'; |
39 | |
40 | ok.innerHTML = '完成'; |
41 | resut.innerHTML = '取消'; |
42 | |
43 | //赋值translate |
44 | province.style.transform = 'translateY(80px)'; |
45 | city.style.transform = 'translateY(80px)'; |
46 | county.style.transform = 'translateY(80px)'; |
47 | //给三个盒子绑定滑动事件 |
48 | touchGo([province,city,county]); |
49 | div.appendChild(cur); |
50 | div.appendChild(ok); |
51 | div.appendChild(resut); |
52 | div.appendChild(province); |
53 | div.appendChild(city); |
54 | div.appendChild(county); |
55 | document.body.appendChild(div); |
56 | } |
57 | |
58 | |
59 | //填充数据 |
60 | fillIn(province,city,county); |
61 | |
62 | |
63 | setTimeout(function(){ |
64 | div.style.transform = 'translateY(-200px)'; |
65 | },0); |
66 | |
67 | //确认选择 |
68 | ok.addEventListener('touchend',function(){ |
69 | |
70 | var one = province.querySelectorAll('span')[oneNum], |
71 | two = city.querySelectorAll('span')[twoNum] || null, |
72 | three = county.querySelectorAll('span')[threeNum] || null, |
73 | selId, |
74 | oneHtml, |
75 | twoHtml, |
76 | threeHtml; |
77 | |
78 | oneHtml = one.innerHTML || ''; |
79 | twoHtml = two ? two.innerHTML : ''; |
80 | threeHtml = three ? three.innerHTML : ''; |
81 | |
82 | sel.innerHTML = oneHtml + twoHtml + threeHtml; |
83 | |
84 | |
85 | oneNodeValue = one ? one.attributes['data-id'].nodeValue : ''; |
86 | twoNodeValue = two ? two.attributes['data-id'].nodeValue : ''; |
87 | threeNodeValue = three ? three.attributes['data-id'].nodeValue : ''; |
88 | selId = oneNodeValue + twoNodeValue + threeNodeValue + ''; |
89 | |
90 | sel.setAttribute('data-id',selId); |
91 | |
92 | div.style.transform = 'translateY(0)'; |
93 | |
94 | createDivFlag = true; |
95 | |
96 | }) |
97 | |
98 | //取消选择 |
99 | resut.addEventListener('touchend',function(){ |
100 | |
101 | div.style.transform = 'translateY(0)'; |
102 | |
103 | createDivFlag = true; |
104 | |
105 | }) |
106 | |
107 | createDivFlag = false; |
108 | |
109 | } |
110 | |
111 | |
112 | |
113 | //给三级菜单填充内容 |
114 | function fillIn(province,city,county){ |
115 | var proStart,cityStart; |
116 | |
117 | //先将内容清空,避免后面累加 |
118 | province.innerHTML = ''; |
119 | city.innerHTML = ''; |
120 | county.innerHTML = ''; |
121 | |
122 | //初始化位置 |
123 | for(k in region.p['000000']){ |
124 | var span = document.createElement('span'); |
125 | span.setAttribute("data-id",k); |
126 | span.innerHTML = region.p['000000'][k]; |
127 | province.appendChild(span); |
128 | } |
129 | |
130 | proStart = province.querySelector('span').attributes['data-id'].nodeValue; |
131 | |
132 | cityBegin(proStart,city); |
133 | |
134 | cityStart = city.querySelector('span').attributes['data-id'].nodeValue; |
135 | |
136 | countyBegin(cityStart,county); |
137 | |
138 | //初始化时自动先获取一遍初始值 |
139 | for(var i = 0; i < arguments.length; i += 1){ |
140 | |
141 | //判断第二次打开,是否需要渲染二三级 |
142 | if(arguments[i].querySelector('span')){ |
143 | |
144 | selectCity(arguments[i]); |
145 | |
146 | } |
147 | } |
148 | |
149 | } |
150 | |
151 | //二级内容 |
152 | function cityBegin(proStart,city){ |
153 | var flag = true; |
154 | |
155 | if(region.c[proStart]){ |
156 | |
157 | for(n in region.c[proStart]){ |
158 | var span = document.createElement('span'); |
159 | span.setAttribute('data-id',n); |
160 | span.innerHTML = region.c[proStart][n]; |
161 | city.appendChild(span); |
162 | } |
163 | |
164 | }else{ |
165 | flag = false; |
166 | } |
167 | |
168 | return flag; |
169 | |
170 | } |
171 | |
172 | |
173 | //三级内容 |
174 | function countyBegin(cityStart,county){ |
175 | for(j in region.d[cityStart]){ |
176 | var span = document.createElement('span'); |
177 | span.setAttribute('data-id',j); |
178 | span.innerHTML = region.d[cityStart][j]; |
179 | county.appendChild(span); |
180 | } |
181 | } |
182 | |
183 | |
184 | //为三级菜单绑定拓展事件 |
185 | function touchGo(objArr){ |
186 | |
187 | for(var i = 0; i < objArr.length; i += 1){ |
188 | |
189 | actionSwiper(objArr[i]); |
190 | |
191 | } |
192 | |
193 | //为三级菜单绑定事件 |
194 | function actionSwiper(obj){ |
195 | |
196 | var startPosition,movePosition,endPosition,deletaY,transY; |
197 | |
198 | //tochBegin,touchOn,touchStop;三个状态判断 |
199 | obj.addEventListener('touchstart',function(event){ |
200 | if(touchStop){ |
201 | touchStop = false; |
202 | var touch = event.touches[0]; |
203 | startPosition = { |
204 | x : touch.pageX, |
205 | y : touch.pageY |
206 | } |
207 | transY = Number(translateXY(obj)); |
208 | objHeight = obj.offsetHeight; |
209 | tochBegin = true; |
210 | } |
211 | |
212 | }) |
213 | |
214 | |
215 | //80 为两个span的高度,初始化时,距离上方80px |
216 | //200 为自定义select的高度。 |
217 | obj.addEventListener('touchmove',function(event){ |
218 | |
219 | if(tochBegin){ |
220 | |
221 | touchOn = true; |
222 | |
223 | var touch = event.touches[0]; |
224 | |
225 | movePosition = { |
226 | x : touch.pageX, |
227 | y : touch.pageY |
228 | }; |
229 | |
230 | deletaY = movePosition.y - startPosition.y + transY; |
231 | |
232 | //向上划上限 |
233 | //deletaY = deletaY > -objHeight - 80 + 200 ? deletaY : -objHeight - 80 + 200; |
234 | deletaY = deletaY > -objHeight + 120 ? deletaY : -objHeight + 120; |
235 | |
236 | //向下滑下限 |
237 | deletaY = deletaY > 80 ? 80 : deletaY; |
238 | |
239 | |
240 | obj.style.transform = 'translateY('+ deletaY +'px)'; |
241 | |
242 | //禁止浏览器默认黑色背景出现 |
243 | event.preventDefault(); |
244 | |
245 | |
246 | } |
247 | |
248 | }) |
249 | |
250 | obj.addEventListener('touchend',function(){ |
251 | |
252 | if(touchOn){ |
253 | |
254 | touchStop = true; |
255 | |
256 | var touch = event.changedTouches[0]; |
257 | |
258 | endPosition = { |
259 | x : touch.pageX, |
260 | y : touch.pageY |
261 | }; |
262 | |
263 | setTimeout(function(){ |
264 | objTouchEnd(obj); |
265 | },100); |
266 | |
267 | } |
268 | |
269 | }) |
270 | |
271 | //滑动结束后处理函数 |
272 | function objTouchEnd(obj){ |
273 | |
274 | var objTranslateY = parseInt(Number(translateXY(obj))), |
275 | objType = '', |
276 | timer = null; |
277 | |
278 | if(endPosition.y - startPosition.y > 0){ |
279 | objType = 'down'; |
280 | }else{ |
281 | objType = 'up'; |
282 | } |
283 | |
284 | timer = setInterval(function(){ |
285 | |
286 | |
287 | if(objType == 'up'){ |
288 | if(Number(translateXY(obj)) % 40 != 0){ |
289 | objTranslateY -= 1; |
290 | } |
291 | } |
292 | |
293 | if(objType == 'down'){ |
294 | if(Number(translateXY(obj)) % 40 != 0){ |
295 | objTranslateY += 1; |
296 | } |
297 | } |
298 | |
299 | obj.style.transform = 'translateY('+ objTranslateY +'px)'; |
300 | |
301 | if(Number(translateXY(obj)) % 40 == 0){ |
302 | |
303 | clearInterval(timer); |
304 | timer = null; |
305 | |
306 | tochBegin = false; |
307 | |
308 | //确定选择 |
309 | selectCity(obj); |
310 | |
311 | } |
312 | |
313 | |
314 | },20); |
315 | |
316 | |
317 | |
318 | } |
319 | |
320 | } |
321 | |
322 | } |
323 | |
324 | |
325 | //获取设置选项 |
326 | function selectCity(obj){ |
327 | //80+80=160 Y为80 的时候是第一个 |
328 | var objTranslateY = Number(translateXY(obj)), |
329 | one, |
330 | oneId, |
331 | onText, |
332 | two, |
333 | twoId, |
334 | twoText, |
335 | three, |
336 | threeId, |
337 | threeText, |
338 | twoGoThree; |
339 | |
340 | if(obj == province){ |
341 | |
342 | oneNum = Math.abs((objTranslateY + 80 - 160) / 40); |
343 | |
344 | //获取序号 |
345 | one = province.querySelectorAll('span')[oneNum]; |
346 | |
347 | //获取自定义属性值 |
348 | oneId = one.attributes['data-id'].nodeValue; |
349 | |
350 | //清空下一级内容 |
351 | city.innerHTML = ''; |
352 | |
353 | //重置下一级translateY |
354 | city.style.transform = 'translateY(80px)'; |
355 | |
356 | //重置第三级translateY |
357 | county.style.transform = 'translateY(80px)'; |
358 | |
359 | //填充下一级内容 |
360 | twoGoThree = cityBegin(oneId,city); |
361 | |
362 | //清空第三项 |
363 | county.innerHTML = ''; |
364 | |
365 | //默认第三级默认第二级第一项 |
366 | if(twoGoThree){ |
367 | countyBegin(city.querySelector('span').attributes['data-id'].nodeValue,county); |
368 | } |
369 | |
370 | } |
371 | |
372 | if(obj == city){ |
373 | |
374 | twoNum = Math.abs((objTranslateY + 80 - 160) / 40); |
375 | |
376 | //获取序号 |
377 | two = city.querySelectorAll('span')[twoNum]; |
378 | |
379 | //获取选中选项自定义属性值 |
380 | twoId = two.attributes['data-id'].nodeValue; |
381 | |
382 | //清空第三项值 |
383 | county.innerHTML = ''; |
384 | |
385 | //重置第三项translateY |
386 | county.style.transform = 'translateY(80px)'; |
387 | |
388 | //填充第三项 |
389 | if(twoId){ |
390 | countyBegin(twoId,county); |
391 | } |
392 | |
393 | } |
394 | |
395 | if(obj == county){ |
396 | |
397 | threeNum = Math.abs((objTranslateY + 80 - 160) / 40); |
398 | |
399 | //获取序号 |
400 | three = county.querySelectorAll('span')[threeNum]; |
401 | |
402 | //获取选中选项自定义属性值 |
403 | threeId = three.attributes['data-id'].nodeValue; |
404 | |
405 | } |
406 | |
407 | } |
408 | |
409 | //获取translateY值 |
410 | function translateXY(obj){ |
411 | var beeTransform = obj.style.transform.replace(/\s/g,''); |
412 | beeTransform = beeTransform.replace('translateY',''); |
413 | beeTransform = beeTransform.slice(1,-1); |
414 | beeTransform = beeTransform.replace('px',''); |
415 | return beeTransform; |
416 | } |
417 | |
418 | })() |
在线运行 在线运行请在谷歌浏览器调试模式下模拟手机环境运行。暂未进行仔细测试,如有问题,欢迎留言共同探讨。
移动端纯原生JS不依赖ajax后台服务器实现省市县三级联动的更多相关文章
- 纯原生js移动端图片压缩上传插件
前段时间,同事又来咨询一个问题了,说手机端动不动拍照就好几M高清大图,上传服务器太慢,问问我有没有可以压缩图片并上传的js插件,当然手头上没有,别慌,我去网上搜一搜. 结果呢,呵呵...诶~又全是基于 ...
- 移动端lCalendar纯原生js日期时间选择器
网上找过很多的移动端基于zepto或jquery的日期选择器,在实际产品中也用过一两种,觉得都不太尽如人意,后来果断选择了H5自己的日期input表单,觉得还可以,至少不用引用第三方插件了,性能也不错 ...
- ThinkPHP 中使用 IS_AJAX 判断原生 JS 中的 Ajax 出现问题
问题: 在 ThinkPHP 中使用原生 js 发起 Ajax 请求的时候.在控制器无法使用 IS_AJAX 进行判断.而使用 jQuery 中的 ajax 是没有问题的. 在ThinkPHP中.有一 ...
- 省市县三级联动js代码
省市县三级联动菜单,JS全国省市县(区)联动代码,一般可以用于用户注册或分类信息二手交易网站,需要的朋友直接复制代码就可以用了,不过有朋友反馈说缺少某些城市,具体缺少哪个尚不知,请想用的朋友自己补全吧 ...
- 纯原生js移动端城市选择插件
接着上一篇纯js移动端日期选择插件,话说今天同事又来咨询省市县联动的效果在移动端中如何实现,还是老样子,百度上一搜,诶~又全是基于jquery.zepto的,更加可恨的是大多数都是PC版的,三个sel ...
- 类似jQuery的原生JS封装的ajax方法
一,前言: 前文,我们介绍了ajax的原理和核心内容,主要讲的是ajax从前端到后端的数据传递的整个过程. Ajax工作原理和原生JS的ajax封装 真正的核心就是这段代码: var xhr = ne ...
- 原生JS写的ajax函数
参照JQuery中的ajax功能,用原生JS写了一个ajax,功能相对JQuery要少很多,不过基本功能都有,包括JSONP. 调用的方式分为两种: 1. ajax(url, {}); 2. ajax ...
- ajax(省,市,县)三级联动
下面我们用Jquery,ajax,做一个省,市,县的三级联动: 下面是我做三级联动下拉的步骤以及逻辑 第一步:先做一个省市区表格 第二步:建个PHP页面显示用我是在<body>里放< ...
- 第八篇 一个用JS写的省市县三级联动
前些天,做网站用需要用到一个省市县的三级联动,数据要从数据库里面读取,我想了下思路,动手写了下来. 一.思路 js利用Ajax读取控制器里面的函数,利用函数读取存储过程,返回 ...
随机推荐
- 调bug时候应该提高思维深度(多问二十个为什么)
版权声明:本文为博主原创文章,未经博主允许不得转载. (一)关于思维深度 读书时 有的人做一份卷子有一份卷子的收获 有的人做100张卷子只有一份卷子的收获 写代码时 有的人调一个Bug可以收获多方面的 ...
- CouldnotcreateServerSocketonaddress0.0.0.0/0.0.0.0:9083
错误记录 安装的时候遇到了如下错误 Exception in thread "main" org.apache.thrift.transport.TTransportExcepti ...
- C语言 大小端 字节对齐
参考:http://www.cnblogs.com/graphics/archive/2011/04/22/2010662.html 1. 大端序:数据的高位字节存放在地址的低端,低位字节存放在地址的 ...
- 解决mac插入U盘不显示标识问题
有时候,我们在使用U盘的时候,如果未能正常退出U盘,下次插入U盘可能会不显示U盘. 解决办法如下,打开Finder-->偏好设置,设置 成功解决问题.
- 能够免费做商业站点的CMS讨论
眼下国内使用过PHPCMS DEDECMS织梦 科讯CMS 帝国.Discuz.Ecshop等,可是他们都是个人非盈利免费,商业.政府.机构授权收费. 使用什么CMS能够免费做商业站点呢? ...
- Foundation 学习
官网 Foundation是个跟bootstrap齐名的前端框架. 移动优先,响应式,最低支持IE8. html+css+jq构建 网格Grid Basic: .row父容器 子元素类.column ...
- Ext.Net 使用总结之查询条件中的起始日期
2.关于查询条件中起始日期的布局方式 首先上一张图,来展示一下我的查询条件的布局,如下: 大多数时候,我们的查询条件都是一个条件占一个格子,但也有不同的时候,如:查询条件是起始日期,则需要将这两个条件 ...
- Kettle 学习笔记
一直用SSIS做ETL,越来越感觉这玩意不是亲生的.因此萌生换ETL工具的想法,不过Kettle社区版没什么调度系统,貌似错误处理也不是很方便,且先了解吧. 本文简略的记录了整个软件的使用流程. 开始 ...
- jvm栈和堆详解
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...
- .Net平台-MVP模式再探(二)
PS: 本文与 上一遍文章 没有什么必然的联系,可以说是对于MVP的一定的加深,或许在理解上比上一篇多有点难度. 正文 一.简单讲讲MVP是什么玩意儿 如果从层次关系来讲,MVP属于P ...