首页 > 代码库 > 开源原生JavaScript插件-CJPCD(省市区联动)
开源原生JavaScript插件-CJPCD(省市区联动)
一、前言
上两篇博客笔者对 JavaScript Module 模式,闭包等知识点做了简单介绍之后,我们今天开始正式开发一款属于自己的 JavaScript 插件。由于最近项目刚好用到地区选择这一块的功能。网上有许多类似插件,但是有些需求还是有些出入,所以就自己动手写了一个。思路是共通的但是实现和细节肯定会有所不同,我们重点放在代码介绍上。笔者已经将其上传到 github,大家可以下载使用,也可以把源码拷下来参考,路过的朋友顺手 star 哦。
二、补充知识
当前插件版本为1.0.1,能满足最常见的使用方式,后续笔者将会继续完善该插件。包括优化或者功能拓展,也希望使用过程中发现问题,或者有改进意见的朋友,可以帮忙指出。
源码浅析
我们先来看下核心代码(部分伪代码)
; (function(){ ‘use strict‘; var CJPCD = function (provinceId,cityId,districtId){ if (!(this instanceof CJPCD)) return new CJPCD(provinceId,cityId,districtId); var c = this; //code... return { setUp:c.set_value,//设置值 reSet:c.builder_init,//重置 getValue:c.get_value//获取值 }; }; CJPCD.prototype={ cj_areas_json: xxx(省市区json), cj_municipalities: xxx(直辖市数组) }; window.CJPCD = CJPCD; }());
可以看出我们采用的是闭包 + 面向对象的思维模式来进行开发的。这里所涉及的重复的知识点笔者将不再介绍,如果有不是很了解的朋友请移步上两篇文章《浅析 JavaScript 中的闭包(Closures)》和《如何开发原生的 JavaScript 插件(知识点+写法)》。我们把讨论的重点放在前两篇文章中没有介绍到的知识点上。
我们先来看下下面这句代码:
‘use strict‘;
字面上的意义来理解就是:“使用严格模式”,顾名思义这行代码开启了 Javascript 的严格模式。开启后有下面几个作用:
1.消除一些不严谨的语法,例如:
num = 1;//报错
不开启严格模式前上面的代码相当于隐式声明了一个全局变量,但是开启之后,全局变量只能显示声明,所以代码报错
2.提编译效率,增加运行速度
该模式在 ECMAscript 5 添加,包括IE 10及更高版本的IE,以及其他主流浏览器都支持该模式。而老版本的浏览器会将其识别为普通字符串,而进行忽略。
调用方式(两种): 第一种是放在脚本文件的第一行,表示整个脚本以严格模式运行。 第二种是放在函数内部第一行,表示整个函数以严格模式运行。
由此可以看出我们的插件(组件)是使用严格模式运行的。
3.注意点: 使用严格模式之后 this 关键字不允许指向全局对象。
我们通过代码来看可能会直观点。我们先来看下非严格模式下的代码
function test(){ console.log(this); } test();
运行结果如下
我们将代码加上严格模式的限制
function test(){ ‘use strict‘; console.log(this); } test();//undefined
所以当我们使用了构造函数后,我们就应当注意需要使用 new 运算
function test(){ ‘use strict‘; console.log(this); } var t = new test();
限于篇幅,关于严格模式的介绍先到这了,有兴趣深入了解的朋友可以参考下面文章:
Strict Model
三、代码部分
基本代码
; (function(){ ‘use strict‘; var CJPCD = function (provinceId,cityId,districtId) { if (!(this instanceof CJPCD)) return new CJPCD(provinceId,cityId,districtId); var c = this; //初始化 c.province = document.getElementById(provinceId); c.city = document.getElementById(cityId); c.district = document.getElementById(districtId); //创建省市区数据 c.builder_init = function(){ //初始化先创建省份数据 c.province_builder(); c.bindChangeEvent(); //初始化触发 c.province.onchange(); }; //设置自定义值 c.set_value = http://www.mamicode.com/function(province,city,district){ //增强容错性-这里有点不严谨,后续完善 if(province.indexOf("省")== -1) province = _type_format(province,"省"); if (city.indexOf("市")== -1) city += "市"; //初始化先创建省份数据 c.province_builder(); c.province.value = province; c.city_builder(); c.city.value = city; c.district_builder(); c.district.value = district; //绑定事件 c.bindChangeEvent(); }; //绑定事件 c.bindChangeEvent = function(){ c.province.onchange=function(){ c.city_builder(); c.city.onchange(); }; c.city.onchange=function(){ c.district_builder(); }; }; //生成省数据 c.province_builder = function(){ c.province.innerHTML=""; //创建子元素 c.optionFactory(c.cj_areas_json,c.province,"prov_index","省"); }; //生成市数据 c.city_builder = function(){ c.city.innerHTML = ""; //获取当前选中省份的索引 var pro_Id = c.province.options[c.province.selectedIndex].getAttribute("prov_index"); var city_obj = c.cj_areas_json[pro_Id].city; //创建子元素 c.optionFactory(city_obj,c.city,"city_index","市"); }; //生成区数据 c.district_builder = function(){ c.district.innerHTML = ""; var pro_Id = c.province.options[c.province.selectedIndex].getAttribute("prov_index"); var city_Id = c.city.options[c.city.selectedIndex].getAttribute("city_index"); var district_obj = c.cj_areas_json[pro_Id].city[city_Id].area; //创建子元素 c.optionFactory(district_obj,c.district,""); }; //通用创建子函数方法 c.optionFactory = function(json_data,parent_obj,attr_name,type){ /* 数据源没有 "省","市"(节省空间) 我们需要自己加(排除四个直辖市) 区,县没有规律可循,所以数据源是完整的 */ var option; for (var i = 0; i < json_data.length; i++) { option = document.createElement("option"); if(attr_name) option.setAttribute(attr_name,i); var name = !json_data[i].name ? json_data[i] : json_data[i].name; //加上后缀之前要排除直辖市和自治区 option.innerHTML = c.type_format(name,type); parent_obj.appendChild(option); }; option = null; }; //加上后缀之前要排除直辖市,还有数据源上的特殊元素"省" c.type_format = function(name,type){ if (!type) return name; for(var item in c.cj_municipalities){ if(c.cj_municipalities[item] == name){ type = ""; break }; }; return name + type; }; c.get_value = function(valueType = "json"){ var province_val = c.province.options[c.province.selectedIndex].value; var city_val = c.city.options[c.city.selectedIndex].value; var district_val = c.district.options[c.district.selectedIndex].value; if(province_val == "省"||city_val == "市"||district_val == "区") return null; if(valueType == "json") return {"province":province_val,"city":city_val,"district":district_val}; else return province_val + valueType + city_val + valueType + district_val; }; //内部初始化 c.builder_init(); //提供API给外部使用 return { setUp:c.set_value,//设置值 reSet:c.builder_init,//重置 getValue:c.get_value//获取值 }; }; //属性 CJPCD.prototype={ //篇幅限制只贴出部分(北京和天津)数据 cj_areas_json : [{ "name": "省", "city": [{ "name": "市", "area": ["区"] }] },{ "name": "北京", "city": [{ "name": "北京", "area": ["东城区", "西城区", "崇文区", "宣武区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区", "平谷区", "怀柔区", "密云县", "延庆县"] }] }, { "name": "天津", "city": [{ "name": "天津", "area": ["和平区", "河东区", "河西区", "南开区", "河北区", "红桥区", "塘沽区", "汉沽区", "大港区", "东丽区", "西青区", "津南区", "北辰区", "武清区", "宝坻区", "宁河县", "静海县", "蓟 县"] }] }], cj_municipalities : ["北京","上海","天津","重庆","内蒙古","新疆","宁夏","西藏","广西","澳门","香港","台湾","钓鱼岛","省","市"] }; window.CJPCD = CJPCD; }());
源码已经基本都有加上注释了,在这里我就不累述了。我们来看下如何使它。
四、CJPCD的使用
如果你是从 github 下载完整的文件包的话,你可以看到介绍文档。内容同下:
最简单的使用只需要两个步骤:
-
放置容器,引入依赖
<!--存放省市区数据的三个 select--> <select id="province"></select> <select id="city"></select> <select id="district"></select> <!--引入插件--> <script src="http://www.mamicode.com/cj-pcd-1.0.1.min.js"></script>
-
调用方法
var pcd = new CJPCD(‘province‘,‘city‘,‘district‘);
效果如下图
API
-
初始化
/** provinceId :省份 select id cityId :城市 select id districtId :县区 select id */ var pcd = new CJPCD(provinceId,cityId,districtId);
-
设置初始值
/** provinceName :省份名称 cityName :城市名称 districtName :县区名称 注意:前提是必须初始化 */ pcd.setUp(‘provinceName‘,‘cityName‘,‘districtName‘);
eg:
var pcd = new CJPCD(‘provinceId‘,‘cityId‘,‘districtId‘);//先初始化 pcd.setUp("广东省","广州市","越秀区");//设置值
使用场景:设置值一般用于地址信息的修改
-
重置
pcd.reSet();
-
获取值
pcd.getValue(‘json‘);//指定以json对象返回 pcd.getValue();//默认以json对象返回,效果同上
json 对象返回如下:
{"province":"广东省","city":"广州市","district":"越秀区"};
指定拼接成一定格式的字符串进行返回,以逗号为例(当然也可以使用其他字符串)
pcd.getValue(‘,‘);//结果使用逗号拼接成字符串
返回值如下
"广东省,广州市,越秀区"
-
补充说明
如果业务中涉及多组地址选择,请创建多个 CJPCD 实例 如
var pcd1 = new CJPCD(provinceId1,cityId1,districtId1); var pcd2 = new CJPCD(provinceId2,cityId2,districtId2); ... var pcdn = new CJPCD(provinceIdn,cityIdn,districtIdn);
使用案例如下:
限于笔者技术,文章观点难免有不当之处,希望发现问题的朋友帮忙指正,笔者将会及时更新。也请转载的朋友注明文章出处并附上原文链接,以便读者能及时获取到文章更新后的内容,以免误导读者。笔者力求避免写些晦涩难懂的文章(虽然也有人说这样显得高逼格,专业),尽量使用简单的用词和例子来帮助理解。如果表达上有好的建议的话也希望朋友们在评论处指出。
本文为作者原创,转载请注明出处! Cboyce
<style>html,div,span,applet,object,iframe,h2,h3,h4,h5,h6,p,blockquote,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video { margin: 0; padding: 0; border: 0 } body>*:first-child { margin-top: 0 !important } body>*:last-child { margin-bottom: 0 !important } p,blockquote,ul,ol,dl { margin: 15px 0 } h2,h3,h4,h5,h6 { margin: 20px 0 10px; padding: 0; font-weight: bold } h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code { font-size: inherit } h2 { font-size: 24px; border-bottom: 1px solid #ccc; color: #000 } h3 { font-size: 18px } h4 { font-size: 16px } h5 { font-size: 14px } h6 { color: #777; font-size: 14px } a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6 { margin-top: 0; padding-top: 0 } h2+p,h3+p,h4+p,h5+p,h6+p { margin-top: 10px } a { color: #4183C4; text-decoration: none } a:hover { text-decoration: underline } ul,ol { padding-left: 30px } ul li>:first-child,ol li>:first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type { margin-top: 0px } ul ul,ul ol,ol ol,ol ul { margin-bottom: 0 } dl { padding: 0 } dl dt { font-size: 14px; font-weight: bold; font-style: italic; padding: 0; margin: 15px 0 5px } dl dt:first-child { padding: 0 } dl dt>:first-child { margin-top: 0px } dl dt>:last-child { margin-bottom: 0px } dl dd { margin: 0 0 15px; padding: 0 15px } dl dd>:first-child { margin-top: 0px } dl dd>:last-child { margin-bottom: 0px } kbd { background-color: #DDDDDD; background-image: linear-gradient(#F1F1F1, #DDDDDD); background-repeat: repeat-x; border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD; border-style: solid; border-width: 1px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; line-height: 10px; padding: 1px 4px } blockquote { border-left: 4px solid #DDD; padding: 0 15px; color: #777 } blockquote>:first-child { margin-top: 0px } blockquote>:last-child { margin-bottom: 0px } hr { clear: both; margin: 15px 0; height: 0px; overflow: hidden; border: none; background: transparent; border-bottom: 4px solid #ddd; padding: 0 } img { max-width: 100% } .cj-img-container { text-align: center }</style>开源原生JavaScript插件-CJPCD(省市区联动)