乐趣区

关于javascript:基于ES6面向对象封装Cascader级联选择器组件

以省市区数据为例,组件成果如下:

技术亮点:

1. 联合前后端通信,实现热更新式动静渲染数据
2. 利用 setter,getter 在组件中设置拜访器属性
3. 利用事件侦听,抛发实现级联成果
4. 实现自定义组件构造及款式

开发思路:

1.ajax 申请:1) 页面初始化申请只申请省级数据,且整个渲染及交互过程中省级数据接口只会申请一次。2) 回调函数 loadHandler 中:判断本次 ajax 申请是哪个接口,如果是申请省级数据,则当省级数据获取后,利用组件实例化对象的 setData 办法,渲染省级组件,同时级联式去申请市级,县级的数据并渲染;如果本次申请市级接口则只渲染市级,县级数据,省级数据不变。3) 给组件绑定 click 侦听事件,当点击组件时抛发数据,携带点击的组件名及以后选中的参数;同理利用上述 2) 中的形式判断点击的是哪一级的数据,从而选择性申请市级,县级数据或者只申请县级数据
2.setter,getter:给组件中的 bool 设置为拜访器属性,当点击事件触发时,实现 bool 值取反,同时 ul 列表款式绑定 bool 值实现动静扭转

组件代码如下:

import Component from "./Component.js";
// 该组件的超类,次要作用是申明了 elem 属性为 div 元素,并创立 appendTo 插入指标页面指定的 dom 元素中的办法
import Utils from "./Utils.js";
// 公共办法, 次要调用其中 Utils.addCSS 办法将款式退出指标页面的样式表中

export default class Label extends Component{
  list;
  _bool = false;// 拜访器属性:setter 和 getter
  styleBool = false;
  name;
  constructor(_name) {super();
    this.name = _name;
    this.elem.className = "dropup";
    // 初始化创立组件时,只会给组件设置 css 款式及侦听元素的点击事件
    this.elem.addEventListener("click", (e) => this.clickHandler(e));
    // 渲染组件款式
    this.setStyle();}
  render() {
    // 渲染组件次要 DOM 构造
    this.elem.innerHTML = "";
    this.elem.innerHTML = `    
        <div class="show">
          ${this.list[0]}
        </div>
        <ul class="ul1 ul2">
          ${this.list.reduce(function (value,item) {
            value += "<li>" + item + "</li>";
            return value;
           },"")}
        </ul>
  `
  }
  clickHandler(e) {
    this.bool = !this.bool;
    if (e.target.nodeName === "LI") {this.elem.children[0].textContent = "";
      this.elem.children[0].textContent = e.target.textContent;
      // 抛发事件时携带数据,以后展现的是哪条数据,以及属于哪一级
      var evt=new Event("change");
      evt.data = this.elem.children[0].textContent;
      evt.name = this.name;
      this.dispatchEvent(evt);
    }
  }
  setData(_list) {
    this.list = _list;
    this.render();
    this.setStyle();
    this.bool = false;
  }
  set bool(value){if (value) {this.elem.children[1].className = "ul1";
      } else {this.elem.children[1].className = "ul1 ul2";
      }
      this._bool=value;
  }
  get bool(){return this._bool;}
  setStyle() {if (this.styleBool) return;
    Utils.addCSS(".dropup", {
      width: "120px",
      height: "30px",
      lineHeight: "30px",
      borderRadius: "4px",
      border: "1px solid  rgba(0,0,0,.15)",
      userSelect: "none",
      textAlign: "center",
      float: "left",
      marginLeft:"10px",
    });
    Utils.addCSS(".ul1", {
      listStyle: "none",
      padding: "0",
      border: "1px solid rgba(0,0,0,.15)",
      borderRadius: "4px",
      boxShadow: "0 6px 12px rgba(0,0,0,.175)",
      margin: "2px 0 0",
      fontSize: "14px",
      textAlign: "left",
      transition: "all 0.2s",
      transitionProperty:"max-height",
      maxHeight: "500px",
      overflow: "auto",
    });
    Utils.addCSS(".ul2", {
      maxHeight: "0px",
      border: "none",
    });
    Utils.addCSS("li", {padding: "0px 10px",});
    Utils.addCSS("li:hover", {backgroundColor: "rgba(0,0,0,0.15)",
    });
    this.styleBool = true;
  }
}

主页面 js 局部代码如下:

    import Label from "./js/Label.js"
    // 须要展现的省市县及源数据
    var provinceArr,cityArr,countyArr;
    // 省市区的实例化对象
    var province,city,county;
    init();
    function init(){render();// 渲染出省市区的组件对象
      ajax("province");// 初始化申请省一级数据,从而触发级联反馈,loadhandler 中申请市,区一级数据
      // 整个过程中 province 只调用一次
    }
    function render(){
      // 创立省市区组件时不增加数据,对立在失去数据后,用 setData 增加数据渲染组件
      province=new Label("province");
      province.addEventListener("change",changeHandler);
      province.appendTo(".setPos");

      city=new Label("city");
      city.appendTo(".setPos");
      city.addEventListener("change",changeHandler);

      county=new Label("county");
      county.appendTo(".setPos");
      county.addEventListener("change",changeHandler);
    }
    function changeHandler(e){switch (e.name){
        case "province":
          // 点击省一栏时,市,县两级都要变
          // 调用 ajax 申请市的接口,从而触发 loadhandler 中的联级反馈
          ajax("city", {province: e.data});
          break;
        case "city":
          // 点击市一栏时,县级须要变
          // 以后状态下省一级的展现数据
          var curProvince = province.elem.firstElementChild.textContent;
          // 调用 ajax 申请县的接口,从而触发 loadhandler 中的联级反馈
          ajax("county", {
            province: curProvince,
            city: e.data
          });
          break;
      }
    }
    function ajax(name,data){var xhr = new XMLHttpRequest();
      xhr.addEventListener("load", loadHandler);
      switch (name) {
        case "province":// 申请省份,加载第一层的数据
          xhr.open("POST", "http:// 服务端接口 url/province");
          break;
        case "city":// 申请市级,加载第二层数据
          xhr.open("POST", "http:// 服务端接口 url/city");
          break;
        case "county":// 申请县级,加载第三层数据
          xhr.open("POST", "http:// 服务端接口 url/county");
          break;
      }
      data === undefined ? xhr.send() : xhr.send(JSON.stringify(data));
    }
    function loadHandler(e){
        var xhr=e.currentTarget;
        xhr.removeEventListener("load",loadHandler);
        // 通过剪切 responseURL 获取申请的 type 接口类型
        var type=xhr.responseURL.trim().split("?")[0].split("\/").pop();
        var sourceData=JSON.parse(xhr.response);
        switch(type){
          case "province":
            // 当发现申请的是省级数据时, 将省一级的数据渲染到组件上,同时须要去申请市一级的数据
            provinceArr = sourceData;
            ajax("city", {"province": provinceArr[0]
            });
            province.setData(provinceArr);
            break;
          case "city":
            // 当发现申请的是市级数据即市级数据须要更新, 则将市一级的数据渲染到组件上, 同时须要去申请县一级的数据
            cityArr = sourceData;
            var curProvince=province.elem.firstElementChild.textContent.trim();
            ajax("county", {
              "province": curProvince,
              "city": cityArr[0]
            });
            city.setData(cityArr);
            break;
          case "county":
            // 当发现是申请县一级数据时,间接将失去的数据渲染到组件上
            countyArr = sourceData;
            county.setData(countyArr);
        }
    }

接口文档如下:

省级数据:

申请形式:POST

申请地址:http:// 服务端接口 url/province

申请参数格局:无

返回参数格局:

{"res":["北京", "上海", "河北", "山西", "内蒙古"]
}

市级数据:

申请形式:POST

申请地址:http:// 服务端接口 url/city

申请参数格局:

{"province": "北京"}

返回参数格局:

{"res":["北京"]
}

县级数据:

申请形式:POST

申请地址:http:// 服务端接口 url/county

申请参数格局:

{
    "province": "北京",
    "city": "北京"
}

返回参数格局:

{"res":["东城区", "西城区", "崇文区", "宣武区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区", "平谷区", "怀柔区", "密云县", "延庆县", "其余"]
}
退出移动版