開源原生 JavaScript 插件--CJPCD(省市區(qū)聯(lián)動(dòng))

一、前言

上兩篇博客筆者對 JavaScript Module 模式,閉包等知識點(diǎn)做了簡單介紹之后,我們今天開始正式開發(fā)一款屬于自己的 JavaScript 插件。由于最近項(xiàng)目剛好用到地區(qū)選擇這一塊的功能。網(wǎng)上有許多類似插件,但是有些需求還是有些出入,所以就自己動(dòng)手寫了一個(gè)。思路是共通的但是實(shí)現(xiàn)和細(xì)節(jié)肯定會(huì)有所不同,我們重點(diǎn)放在代碼介紹上。筆者已經(jīng)將其上傳到 github,大家可以下載使用,也可以把源碼拷下來參考,路過的朋友順手 star 哦。

二、補(bǔ)充知識

當(dāng)前插件版本為1.0.1,能滿足最常見的使用方式,后續(xù)筆者將會(huì)繼續(xù)完善該插件。包括優(yōu)化或者功能拓展,也希望使用過程中發(fā)現(xiàn)問題,或者有改進(jìn)意見的朋友,可以幫忙指出。

源碼淺析

我們先來看下核心代碼(部分偽代碼)

;
(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,//設(shè)置值
         reSet:c.builder_init,//重置
         getValue:c.get_value//獲取值
        };
    };

    CJPCD.prototype={
        cj_areas_json: xxx(省市區(qū)json),
        cj_municipalities: xxx(直轄市數(shù)組)
    };

    window.CJPCD = CJPCD;
}());

可以看出我們采用的是閉包 + 面向?qū)ο蟮乃季S模式來進(jìn)行開發(fā)的。這里所涉及的重復(fù)的知識點(diǎn)筆者將不再介紹,如果有不是很了解的朋友請移步上兩篇文章《淺析 JavaScript 中的閉包(Closures)》《如何開發(fā)原生的 JavaScript 插件(知識點(diǎn)+寫法)》。我們把討論的重點(diǎn)放在前兩篇文章中沒有介紹到的知識點(diǎn)上。

我們先來看下下面這句代碼:

'use strict';

字面上的意義來理解就是:“使用嚴(yán)格模式”,顧名思義這行代碼開啟了 Javascript 的嚴(yán)格模式。開啟后有下面幾個(gè)作用:

1.消除一些不嚴(yán)謹(jǐn)?shù)恼Z法,例如:

num = 1;//報(bào)錯(cuò)

不開啟嚴(yán)格模式前上面的代碼相當(dāng)于隱式聲明了一個(gè)全局變量,但是開啟之后,全局變量只能顯示聲明,所以代碼報(bào)錯(cuò)

2.提編譯效率,增加運(yùn)行速度

該模式在 ECMAscript 5 添加,包括IE 10及更高版本的IE,以及其他主流瀏覽器都支持該模式。而老版本的瀏覽器會(huì)將其識別為普通字符串,而進(jìn)行忽略。

調(diào)用方式(兩種):
第一種是放在腳本文件的第一行,表示整個(gè)腳本以嚴(yán)格模式運(yùn)行。
第二種是放在函數(shù)內(nèi)部第一行,表示整個(gè)函數(shù)以嚴(yán)格模式運(yùn)行。

由此可以看出我們的插件(組件)是使用嚴(yán)格模式運(yùn)行的。

3.注意點(diǎn): 使用嚴(yán)格模式之后 this 關(guān)鍵字不允許指向全局對象。

我們通過代碼來看可能會(huì)直觀點(diǎn)。我們先來看下非嚴(yán)格模式下的代碼

function test(){
     console.log(this);
}
test();

運(yùn)行結(jié)果如下

運(yùn)行結(jié)果

我們將代碼加上嚴(yán)格模式的限制

function test(){
     'use strict';
     console.log(this);
 }
 test();//undefined
運(yùn)行結(jié)果

所以當(dāng)我們使用了構(gòu)造函數(shù)后,我們就應(yīng)當(dāng)注意需要使用 new 運(yùn)算

 function test(){
     'use strict';
     console.log(this);
 }
 var t = new test();
運(yùn)行結(jié)果

限于篇幅,關(guān)于嚴(yán)格模式的介紹先到這了,有興趣深入了解的朋友可以參考下面文章:

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

    //創(chuàng)建省市區(qū)數(shù)據(jù)
    c.builder_init = function(){      
      //初始化先創(chuàng)建省份數(shù)據(jù)
      c.province_builder();

      c.bindChangeEvent();

      //初始化觸發(fā)
      c.province.onchange();
    };

    //設(shè)置自定義值
    c.set_value = function(province,city,district){

      //增強(qiáng)容錯(cuò)性-這里有點(diǎn)不嚴(yán)謹(jǐn),后續(xù)完善
      if(province.indexOf("省")== -1)
        province = _type_format(province,"省");
      if (city.indexOf("市")== -1)
        city += "市";    

      //初始化先創(chuàng)建省份數(shù)據(jù)
      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();
      };
    };

    //生成省數(shù)據(jù)
    c.province_builder = function(){
      c.province.innerHTML="";
      //創(chuàng)建子元素
      c.optionFactory(c.cj_areas_json,c.province,"prov_index","省");
    };

    //生成市數(shù)據(jù)
    c.city_builder = function(){ 
      c.city.innerHTML = "";
      //獲取當(dāng)前選中省份的索引
      var pro_Id = c.province.options[c.province.selectedIndex].getAttribute("prov_index");
      var city_obj = c.cj_areas_json[pro_Id].city;
      //創(chuàng)建子元素
      c.optionFactory(city_obj,c.city,"city_index","市");
    };

    //生成區(qū)數(shù)據(jù)
    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;

      //創(chuàng)建子元素
      c.optionFactory(district_obj,c.district,"");
    };

    //通用創(chuàng)建子函數(shù)方法
    c.optionFactory = function(json_data,parent_obj,attr_name,type){   
      /*
        數(shù)據(jù)源沒有 "省","市"(節(jié)省空間)
        我們需要自己加(排除四個(gè)直轄市)
        區(qū),縣沒有規(guī)律可循,所以數(shù)據(jù)源是完整的
      */
      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;

        //加上后綴之前要排除直轄市和自治區(qū)
        option.innerHTML =  c.type_format(name,type);
        parent_obj.appendChild(option);
      };
      option = null;
    };

    //加上后綴之前要排除直轄市,還有數(shù)據(jù)源上的特殊元素"省"
    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 == "區(qū)")
        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;
    };
    
    //內(nèi)部初始化
    c.builder_init();

    //提供API給外部使用
    return {
     setUp:c.set_value,//設(shè)置值
     reSet:c.builder_init,//重置
     getValue:c.get_value//獲取值
    };
  };

  //屬性
  CJPCD.prototype={
    //篇幅限制只貼出部分(北京和天津)數(shù)據(jù)
    cj_areas_json : [{ "name": "省", "city": [{ "name": "市", "area": ["區(qū)"] }] },{ "name": "北京", "city": [{ "name": "北京", "area": ["東城區(qū)", "西城區(qū)", "崇文區(qū)", "宣武區(qū)", "朝陽區(qū)", "豐臺(tái)區(qū)", "石景山區(qū)", "海淀區(qū)", "門頭溝區(qū)", "房山區(qū)", "通州區(qū)", "順義區(qū)", "昌平區(qū)", "大興區(qū)", "平谷區(qū)", "懷柔區(qū)", "密云縣", "延慶縣"] }] }, { "name": "天津", "city": [{ "name": "天津", "area": ["和平區(qū)", "河?xùn)|區(qū)", "河西區(qū)", "南開區(qū)", "河北區(qū)", "紅橋區(qū)", "塘沽區(qū)", "漢沽區(qū)", "大港區(qū)", "東麗區(qū)", "西青區(qū)", "津南區(qū)", "北辰區(qū)", "武清區(qū)", "寶坻區(qū)", "寧河縣", "靜??h", "薊  縣"] }] }],

    cj_municipalities : ["北京","上海","天津","重慶","內(nèi)蒙古","新疆","寧夏","西藏","廣西","澳門","香港","臺(tái)灣","釣魚島","省","市"]
  };
    
  window.CJPCD = CJPCD;

}());

源碼已經(jīng)基本都有加上注釋了,在這里我就不累述了。我們來看下如何使它。

四、CJPCD的使用

如果你是從 github 下載完整的文件包的話,你可以看到介紹文檔。內(nèi)容同下:

最簡單的使用只需要兩個(gè)步驟:

  1. 放置容器,引入依賴

     <!--存放省市區(qū)數(shù)據(jù)的三個(gè) select-->
     <select id="province"></select>
     <select id="city"></select>
     <select id="district"></select>
     
     <!--引入插件-->
     <script src="./cj-pcd-1.0.1.min.js"></script>
    
  2. 調(diào)用方法

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

    效果如下圖

界面

API

  1. 初始化

     /**
         provinceId :省份 select id
         cityId     :城市 select id
         districtId :縣區(qū) select id
     */
     var pcd = new CJPCD(provinceId,cityId,districtId);
    
  2. 設(shè)置初始值

     /**
         provinceName :省份名稱
         cityName     :城市名稱
         districtName :縣區(qū)名稱
     
         注意:前提是必須初始化
     */
     pcd.setUp('provinceName','cityName','districtName');
    

    eg:

     var pcd = new CJPCD('provinceId','cityId','districtId');//先初始化
     pcd.setUp("廣東省","廣州市","越秀區(qū)");//設(shè)置值    
    

    使用場景:設(shè)置值一般用于地址信息的修改

  3. 重置

     pcd.reSet();
    
  4. 獲取值

     pcd.getValue('json');//指定以json對象返回
     pcd.getValue();//默認(rèn)以json對象返回,效果同上
    

    json 對象返回如下:

     {"province":"廣東省","city":"廣州市","district":"越秀區(qū)"};
    

    指定拼接成一定格式的字符串進(jìn)行返回,以逗號為例(當(dāng)然也可以使用其他字符串)

     pcd.getValue(',');//結(jié)果使用逗號拼接成字符串
    

    返回值如下

     "廣東省,廣州市,越秀區(qū)"
    
  5. 補(bǔ)充說明

    如果業(yè)務(wù)中涉及多組地址選擇,請創(chuàng)建多個(gè) CJPCD 實(shí)例 如

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

    使用案例如下:

使用案例

限于筆者技術(shù),文章觀點(diǎn)難免有不當(dāng)之處,希望發(fā)現(xiàn)問題的朋友幫忙指正,筆者將會(huì)及時(shí)更新。也請轉(zhuǎn)載的朋友注明文章出處并附上原文鏈接,以便讀者能及時(shí)獲取到文章更新后的內(nèi)容,以免誤導(dǎo)讀者。筆者力求避免寫些晦澀難懂的文章(雖然也有人說這樣顯得高逼格,專業(yè)),盡量使用簡單的用詞和例子來幫助理解。如果表達(dá)上有好的建議的話也希望朋友們在評論處指出。

本文為作者原創(chuàng),轉(zhuǎn)載請注明出處! Cboyce

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡單...
    舟漁行舟閱讀 8,115評論 2 17
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,511評論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,628評論 18 399
  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock閱讀 3,475評論 2 36
  • 今天,爸爸早上去了深圳,后來他在深圳買了今天晚上回來的機(jī)票。然而他很搞笑的,他居然買了下周五的機(jī)票。所以他今...
    九五自尊閱讀 183評論 0 0

友情鏈接更多精彩內(nèi)容