共计 4802 个字符,预计需要花费 13 分钟才能阅读完成。
以省市区数据为例,组件成果如下:
技术亮点:
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":["东城区", "西城区", "崇文区", "宣武区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区", "平谷区", "怀柔区", "密云县", "延庆县", "其余"] | |
} |
正文完
发表至: javascript
2020-11-14