一、前言

上两篇博客笔者对 JavaScript Module 模式,闭包等知识点做了简单介绍之后,我们今天开始正式开发一款属于自己的 JavaScript 插件。由于最近项目刚好用到地区选择这一块的功能。网上有许多类似插件,但是有些需求还是有些出入,所以就自己动手写了一个。思路是共通的但是实现和细节肯定会有所不同,我们重点放在代码介绍上。笔者已经将其上传到 github,大家可以下载使用,也可以把源码拷下来参考,路过的朋友顺手 star 哦。

二、补充知识

当前插件版本为1.0.1,能满足最常见的使用方式,后续笔者将会继续完善该插件。包括优化或者功能拓展,也希望使用过程中发现问题,或者有改进意见的朋友,可以帮忙指出。

源码浅析

我们先来看下核心代码(部分伪代码)

  1. ;
  2. (function(){
  3. 'use strict';
  4. var CJPCD = function (provinceId,cityId,districtId){
  5. if (!(this instanceof CJPCD)) return new CJPCD(provinceId,cityId,districtId);
  6. var c = this;
  7.  
  8. //code...
  9.  
  10. return {
  11. setUp:c.set_value,//设置值
  12. reSet:c.builder_init,//重置
  13. getValue:c.get_value//获取值
  14. };
  15. };
  16.  
  17. CJPCD.prototype={
  18. cj_areas_json: xxx(省市区json),
  19. cj_municipalities: xxx(直辖市数组)
  20. };
  21.  
  22. window.CJPCD = CJPCD;
  23. }());

可以看出我们采用的是闭包 + 面向对象的思维模式来进行开发的。这里所涉及的重复的知识点笔者将不再介绍,如果有不是很了解的朋友请移步上两篇文章《浅析 JavaScript 中的闭包(Closures)》《如何开发原生的 JavaScript 插件(知识点+写法)》。我们把讨论的重点放在前两篇文章中没有介绍到的知识点上。

我们先来看下下面这句代码:

  1. 'use strict';

字面上的意义来理解就是:“使用严格模式”,顾名思义这行代码开启了 Javascript 的严格模式。开启后有下面几个作用:

1.消除一些不严谨的语法,例如:

  1. num = 1;//报错

不开启严格模式前上面的代码相当于隐式声明了一个全局变量,但是开启之后,全局变量只能显示声明,所以代码报错

2.提编译效率,增加运行速度

该模式在 ECMAscript 5 添加,包括IE 10及更高版本的IE,以及其他主流浏览器都支持该模式。而老版本的浏览器会将其识别为普通字符串,而进行忽略。

调用方式(两种): 第一种是放在脚本文件的第一行,表示整个脚本以严格模式运行。 第二种是放在函数内部第一行,表示整个函数以严格模式运行。

由此可以看出我们的插件(组件)是使用严格模式运行的。

3.注意点: 使用严格模式之后 this 关键字不允许指向全局对象。

我们通过代码来看可能会直观点。我们先来看下非严格模式下的代码

  1. function test(){
  2. console.log(this);
  3. }
  4. test();

运行结果如下

我们将代码加上严格模式的限制

  1. function test(){
  2. 'use strict';
  3. console.log(this);
  4. }
  5. test();//undefined

所以当我们使用了构造函数后,我们就应当注意需要使用 new 运算

  1. function test(){
  2. 'use strict';
  3. console.log(this);
  4. }
  5. var t = new test();

限于篇幅,关于严格模式的介绍先到这了,有兴趣深入了解的朋友可以参考下面文章:

Strict Model

三、代码部分

基本代码

  1. ;
  2. (function(){
  3. 'use strict';
  4. var CJPCD = function (provinceId,cityId,districtId) {
  5. if (!(this instanceof CJPCD)) return new CJPCD(provinceId,cityId,districtId);
  6.  
  7. var c = this;
  8.  
  9. //初始化
  10. c.province = document.getElementById(provinceId);
  11. c.city = document.getElementById(cityId);
  12. c.district = document.getElementById(districtId);
  13.  
  14. //创建省市区数据
  15. c.builder_init = function(){
  16. //初始化先创建省份数据
  17. c.province_builder();
  18.  
  19. c.bindChangeEvent();
  20.  
  21. //初始化触发
  22. c.province.onchange();
  23. };
  24.  
  25. //设置自定义值
  26. c.set_value = function(province,city,district){
  27.  
  28. //增强容错性-这里有点不严谨,后续完善
  29. if(province.indexOf("省")== -1)
  30. province = _type_format(province,"省");
  31. if (city.indexOf("市")== -1)
  32. city += "市";
  33.  
  34. //初始化先创建省份数据
  35. c.province_builder();
  36. c.province.value = province;
  37.  
  38. c.city_builder();
  39. c.city.value = city;
  40.  
  41. c.district_builder();
  42. c.district.value = district;
  43.  
  44. //绑定事件
  45. c.bindChangeEvent();
  46. };
  47.  
  48. //绑定事件
  49. c.bindChangeEvent = function(){
  50. c.province.onchange=function(){
  51. c.city_builder();
  52. c.city.onchange();
  53. };
  54. c.city.onchange=function(){
  55. c.district_builder();
  56. };
  57. };
  58.  
  59. //生成省数据
  60. c.province_builder = function(){
  61. c.province.innerHTML="";
  62. //创建子元素
  63. c.optionFactory(c.cj_areas_json,c.province,"prov_index","省");
  64. };
  65.  
  66. //生成市数据
  67. c.city_builder = function(){
  68. c.city.innerHTML = "";
  69. //获取当前选中省份的索引
  70. var pro_Id = c.province.options[c.province.selectedIndex].getAttribute("prov_index");
  71. var city_obj = c.cj_areas_json[pro_Id].city;
  72. //创建子元素
  73. c.optionFactory(city_obj,c.city,"city_index","市");
  74. };
  75.  
  76. //生成区数据
  77. c.district_builder = function(){
  78. c.district.innerHTML = "";
  79. var pro_Id = c.province.options[c.province.selectedIndex].getAttribute("prov_index");
  80. var city_Id = c.city.options[c.city.selectedIndex].getAttribute("city_index");
  81. var district_obj = c.cj_areas_json[pro_Id].city[city_Id].area;
  82.  
  83. //创建子元素
  84. c.optionFactory(district_obj,c.district,"");
  85. };
  86.  
  87. //通用创建子函数方法
  88. c.optionFactory = function(json_data,parent_obj,attr_name,type){
  89. /*
  90. 数据源没有 "省","市"(节省空间)
  91. 我们需要自己加(排除四个直辖市)
  92. 区,县没有规律可循,所以数据源是完整的
  93. */
  94. var option;
  95. for (var i = 0; i < json_data.length; i++) {
  96. option = document.createElement("option");
  97. if(attr_name)
  98. option.setAttribute(attr_name,i);
  99.  
  100. var name = !json_data[i].name ? json_data[i] : json_data[i].name;
  101.  
  102. //加上后缀之前要排除直辖市和自治区
  103. option.innerHTML = c.type_format(name,type);
  104. parent_obj.appendChild(option);
  105. };
  106. option = null;
  107. };
  108.  
  109. //加上后缀之前要排除直辖市,还有数据源上的特殊元素"省"
  110. c.type_format = function(name,type){
  111. if (!type)
  112. return name;
  113.  
  114. for(var item in c.cj_municipalities){
  115. if(c.cj_municipalities[item] == name){
  116. type = "";
  117. break
  118. };
  119. };
  120. return name + type;
  121. };
  122.  
  123. c.get_value = function(valueType = "json"){
  124.  
  125. var province_val = c.province.options[c.province.selectedIndex].value;
  126. var city_val = c.city.options[c.city.selectedIndex].value;
  127. var district_val = c.district.options[c.district.selectedIndex].value;
  128. if(province_val == "省"||city_val == "市"||district_val == "区")
  129. return null;
  130. if(valueType == "json")
  131. return {"province":province_val,"city":city_val,"district":district_val};
  132. else
  133. return province_val + valueType + city_val + valueType + district_val;
  134. };
  135.  
  136. //内部初始化
  137. c.builder_init();
  138.  
  139. //提供API给外部使用
  140. return {
  141. setUp:c.set_value,//设置值
  142. reSet:c.builder_init,//重置
  143. getValue:c.get_value//获取值
  144. };
  145. };
  146.  
  147. //属性
  148. CJPCD.prototype={
  149. //篇幅限制只贴出部分(北京和天津)数据
  150. cj_areas_json : [{ "name": "省", "city": [{ "name": "市", "area": ["区"] }] },{ "name": "北京", "city": [{ "name": "北京", "area": ["东城区", "西城区", "崇文区", "宣武区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区", "平谷区", "怀柔区", "密云县", "延庆县"] }] }, { "name": "天津", "city": [{ "name": "天津", "area": ["和平区", "河东区", "河西区", "南开区", "河北区", "红桥区", "塘沽区", "汉沽区", "大港区", "东丽区", "西青区", "津南区", "北辰区", "武清区", "宝坻区", "宁河县", "静海县", "蓟 县"] }] }],
  151.  
  152. cj_municipalities : ["北京","上海","天津","重庆","内蒙古","新疆","宁夏","西藏","广西","澳门","香港","台湾","钓鱼岛","省","市"]
  153. };
  154.  
  155. window.CJPCD = CJPCD;
  156.  
  157. }());

源码已经基本都有加上注释了,在这里我就不累述了。我们来看下如何使它。

四、CJPCD的使用

如果你是从 github 下载完整的文件包的话,你可以看到介绍文档。内容同下:

最简单的使用只需要两个步骤:

  1. 放置容器,引入依赖

    1. <!--存放省市区数据的三个 select-->
    2. <select id="province"></select>
    3. <select id="city"></select>
    4. <select id="district"></select>
    5.  
    6. <!--引入插件-->
    7. <script src="./cj-pcd-1.0.1.min.js"></script>
  2. 调用方法

    1. var pcd = new CJPCD('province','city','district');

    效果如下图

API

  1. 初始化

    1. /**
    2. provinceId :省份 select id
    3. cityId :城市 select id
    4. districtId :县区 select id
    5. */
    6. var pcd = new CJPCD(provinceId,cityId,districtId);
  2. 设置初始值

    1. /**
    2. provinceName :省份名称
    3. cityName :城市名称
    4. districtName :县区名称
    5.  
    6. 注意:前提是必须初始化
    7. */
    8. pcd.setUp('provinceName','cityName','districtName');

    eg:

    1. var pcd = new CJPCD('provinceId','cityId','districtId');//先初始化
    2. pcd.setUp("广东省","广州市","越秀区");//设置值

    使用场景:设置值一般用于地址信息的修改

  3. 重置

    1. pcd.reSet();
  4. 获取值

    1. pcd.getValue('json');//指定以json对象返回
    2. pcd.getValue();//默认以json对象返回,效果同上

    json 对象返回如下:

    1. {"province":"广东省","city":"广州市","district":"越秀区"};

    指定拼接成一定格式的字符串进行返回,以逗号为例(当然也可以使用其他字符串)

    1. pcd.getValue(',');//结果使用逗号拼接成字符串

    返回值如下

    1. "广东省,广州市,越秀区"
  5. 补充说明

    如果业务中涉及多组地址选择,请创建多个 CJPCD 实例 如

    1. var pcd1 = new CJPCD(provinceId1,cityId1,districtId1);
    2. var pcd2 = new CJPCD(provinceId2,cityId2,districtId2);
    3. ...
    4. var pcdn = new CJPCD(provinceIdn,cityIdn,districtIdn);

    使用案例如下:

限于笔者技术,文章观点难免有不当之处,希望发现问题的朋友帮忙指正,笔者将会及时更新。也请转载的朋友注明文章出处并附上原文链接,以便读者能及时获取到文章更新后的内容,以免误导读者。笔者力求避免写些晦涩难懂的文章(虽然也有人说这样显得高逼格,专业),尽量使用简单的用词和例子来帮助理解。如果表达上有好的建议的话也希望朋友们在评论处指出。

本文为作者原创,转载请注明出处! Cboyce

有些问题回复没办法截图.我就在这里贴了

@循环源圈 博友提出的

1.js函数不能有默认值的问题.  是可以的

2."谷歌浏览器不支持display的none与block的切换" 的问题 .   是支持的(Chrome是很牛X的)

之前的代码并没有写切换

  1. document.getElementById('newcase').onclick=function(){
  2. document.getElementById('btn-group2').style = "display:block";
  3. document.getElementById('row2').style = "display:block";
  4. }

写完切换

  1. var is_show = false;
  2. document.getElementById('newcase').onclick=function(){
  3. if(!is_show){
  4. document.getElementById('btn-group2').style = "display:block";
  5. document.getElementById('row2').style = "display:block";
  6. }else{
  7. document.getElementById('btn-group2').style = "display:none";
  8. document.getElementById('row2').style = "display:none";
  9. }
  10. is_show = !is_show;
  11. }

效果

*:first-child {
margin-top: 0 !important;
}

body>*:last-child {
margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl {
margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}

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

/* LINKS
=============================================================================*/

a {
color: #4183C4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* LISTS
=============================================================================*/

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

/* CODE
=============================================================================*/

kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}

blockquote>:first-child {
margin-top: 0px;
}

blockquote>:last-child {
margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}

/* TABLES
=============================================================================*/

/* IMAGES
=============================================================================*/

img {
max-width: 100%
}
.cj-img-container {
text-align: center;
}
-->

开源原生JavaScript插件-CJPCD(省市区联动)的更多相关文章

  1. 原生JavaScript插件编写指南(转载)

    原生js开发指南 https://www.jianshu.com/p/e65c246beac1 在jQuery大量使用的环境下,目前网上的众多jQuery插件也能基本满足要求,但是在项目具体需求下,有 ...

  2. 开源的JavaScript插件化框架MinimaJS

    本文介绍我开发的一个JavaScript编写的插件化框架——MinimaJS,完全开源,源码下载地址:https://github.com/lorry2018/minimajs.该框架参考OSGi规范 ...

  3. 原生JS插件(超详细)

    作为一个前端er,如果不会写一个小插件,都不好意思说自己是混前端界的.写还不能依赖jquery之类的工具库,否则装得不够高端.那么,如何才能装起来让自己看起来逼格更高呢?当然是利用js纯原生的写法啦. ...

  4. 如何定义一个高逼格的原生JS插件

    插件的需求 我们写代码,并不是所有的业务或者逻辑代码都要抽出来复用.首先,我们得看一下是否需要将一部分经常重复的代码抽象出来,写到一个单独的文件中为以后再次使用.再看一下我们的业务逻辑是否可以为团队服 ...

  5. 如何开发原生的 JavaScript 插件(知识点+写法)

    一.前言 通过 "WWW" 原则我们来了解 JavaScript 插件这个东西 第一个 W "What" -- 是什么?什么是插件,我就不照搬书本上的抽象概念了 ...

  6. 原生JavaScript的省市县三级联动

    三级联动是我们写表单时必不可少的,比如在写收货地址时,就用到他了,最近在看原生JavaScript,从基础写起,待完善,以后再写个jquery版的. <!DOCTYPE html> < ...

  7. 使用原生JavaScript的Canvas实现拖拽式图形绘制,支持画笔、线条、箭头、三角形、矩形、平行四边形、梯形以及多边形和圆形,不依赖任何库和插件,有演示demo

    前言 需要用到图形绘制,没有找到完整的图形绘制实现,所以自己实现了一个 - - 一.实现的功能 1.基于oop思想构建,支持坐标点.线条(由坐标点组成,包含方向).多边形(由多个坐标点组成).圆形(包 ...

  8. 一个基于原生JavaScript开发的、轻量的验证码生成插件

    Vcode.js 一个基于原生JavaScript开发的.轻量的验证码生成插件 V: 1.0.0 DEMO:https://jofunliang.github.io/Vcode.js/example. ...

  9. 原生JS 年月日、省市区 三级联动

    这个算生日日期,因为是从100年前的到现年. <select id="sel_year"></select> <select id="sel ...

随机推荐

  1. 数据库锁机制(一)——概述

    注:内容为自己的推理认知+网络,如有错误和不合理之处,敬请指出. 在多线程环境中我用使用线程锁处理并发问题,而在数据库系统中,并发问题可以细化到事务级别,而DBMS对此的处理方案就是使用锁. 为了适应 ...

  2. ActivityManager: Warning: Activity not started, its current task has been brought to the front 的的问题

    运行android程序的时候提示:ActivityManager: Warning: Activity not started, its current task has been brought t ...

  3. uva 11624 Fire!(搜索)

    开始刷题啦= = 痛并快乐着,学到新东西的感觉其实比看那些无脑的小说.电视剧有意思多了 bfs裸体,关键是先把所有的着火点放入队列,分开一个一个做bfs会超时的 发现vis[][]是多余的,完全可以用 ...

  4. 省常中模拟 Test3 Day1

    tile 贪心 题意:给出一个矩形,用不同字母代表的正方形填充,要求相邻的方块字母不能相同,求字典序(将所有行拼接起来)最小的方案. 初步解法:一开始没怎么想,以为策略是每次填充一个尽量大的正方形.但 ...

  5. LeetCode: divideInteger

    Title: Divide two integers without using multiplication, division and mod operator. If it is overflo ...

  6. liunx环境下的mysql数据库配置文件my.conf内的参数含义

    [client]port = 3306socket = /tmp/mysql.sock [mysqld]port = 3306socket = /tmp/mysql.sock basedir = /u ...

  7. IOS AFNetworking简介及使用

    转:http://www.it165.net/pro/html/201405/13099.html 一AFNetworking简介AFNetworking是一个在IOS开发中使用非常多网络开源库,适用 ...

  8. 新闻类App使用的组件

    UI SlidingMenu:com.jeremyfeinstein.slidingmenu:滑动菜单 ActionBarSherlock:com.actionbarsherlock:Action B ...

  9. C 的 coroutine 库 via 云风的 BLOG

    今天实现了一个 C 用的 coroutine 库. 我相信这个东西已经被无数 C 程序员实现过了, 但是通过 google 找了许多, 或是接口不让我满意, 或是过于重量. 在 Windows 下, ...

  10. js时间日期转时间戳

    var contractstarttimea='2016-01-01'; var contractendtimea='2016-05-01'; var contractstart = Date.par ...