Idea: Each drop-down menu, as a component, can receive a set of data and generate different menu options based on different data contents. The correlation between the three levels is achieved through event throwing. Data is obtained from the background.
When clicking on the province menu to select Shaanxi, the menu component will throw out the current province through event throwing. After knowing the province, you can obtain the city data under the province from the background. And so on.
Realization effect:
## URL: http://10.9.72.245:4010
## Method: "GET"
## Data format:
Request: QueryString
Response: JSON
1. http: //10.9.72.245:4010/getProvince
2. http://10.9.72.245:4010/getCity 3. http://10.9.72.245:4010/getCounty
interface name: /getProvince
Request
: No parameters
Response: { "province":["Beijing","Tianjin","Hebei",...]}
interface name:/getCity
request:?province="Hebei"
response: {"city":["Shijiazhuang" , "Tangshan", "Qinhuangdao",...]}
interface name: /getCounty
request: ?city="Shijiazhuang"
response: {"county":["Chang'an District", "Qiaodong District", "Qiaoxi District",...]}
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script type="module"> import QueryString from './js/QueryString.js'; import DropDownMemu from './js/DropDownMemu.js'; let cityInfo = {}; init(); function init(){ ajax("http://10.9.72.245:4010","getProvince").then(successFunction).catch(failFunction); } //Execute after successful ajax communication: function succeseFunction(_data){ let data = JSON.parse(_data); let key = Object.keys(data)[0]; data = Object.values(data)[0]; if(DropDownMemu.Obj[key]){ DropDownMemu.Obj[key].list = data; DropDownMemu.Obj[key].name = data[0]; }else{ let memu = new DropDownMemu(key); memu.addEventListener("change",changeHandler); memu.list = data; memu.name =data[0]; memu.appendTo("body"); cityInfo[key] = data[0]; } } // When the menu display content changes, receive the event thrown by the menu and obtain the information carried in the event, such as {"province":"Shaanxi"} or {"city":"Xi'an"} function changeHandler(e){ let key = e.currentTarget.label; cityInfo[key] = e.data[key]; let interfaceName; if(e.data.province){ interfaceName = "getCity"; }else if(e.data.city){ interfaceName = "getCounty"; }else{ return } ajax("http://10.9.72.245:4010",interfaceName,cityInfo).then(successeFunction).catch(failFunction); } /* ajax communication: Parameter list: url: background service address interfaceType: interface type, such as "getProvince" data: transmitted data, for example: {"province":"Shaanxi"} Communication type type: Default "GET" request whether to send data in json format: the default is false */ function ajax(url,interfaceType,data,type="get",json=false){ type = type.toUpperCase(); let o = type==="GET"? null : data; if(data) data = json? JSON.stringify(data) : QueryString.stringify(data); else data = ""; return new Promise(function(resolve,reject){ let xhr = new XMLHttpRequest(); xhr.open(type,url + "/" + interfaceType + (type==="GET"? "?"+data : "")); xhr.send(o); xhr.onreadystatechange = function(){ if(xhr.readyState===4 && xhr.status===200){ resolve(xhr.response); }else if(xhr.readyState===4){ resolve(xhr.status); } } xhr.onerror= function(){ reject(xhr.response); } }) } //Execute function failFunction(_err){ when ajax communication fails console.log(_err); } </script> </body> </html>
import Component from "./Component.js"; export default class DropDownMemu extends Component { _list; // Current drop-down menu options. _name; //The currently selected name to display, for example: "Beijing" label; //The label of the current drop-down menu, province city county spanLabel; // Label container spanCaret; // Triangle ul; // Drop-down option container bool=false; // Control mouse events, focus state or selection, do not trigger the mouse slide-in and slide-out effect. //Set the style of the drop-down menu according to different states. static DROPDOWN = Symbol(); static DEFAULT = Symbol(); // Static global variables are stored in this object every time a drop-down menu is created, and each drop-down menu created is managed globally. static Obj = {}; constructor(_label) { super("p"); this.label = _label; //Create HTML structure this.render(); //Set the style this.setStyle(); // The mouse slides in and out and clicks, the focus is out of focus, the click event this.elem.addEventListener("focusin", e =>this.mouseHandler(e)); this.elem.addEventListener("focusout", e =>this.mouseHandler(e)); this.elem.addEventListener("mouseenter", e=>this.mouseHandler(e)); this.elem.addEventListener("mouseleave", e=>this.mouseHandler(e)); this.elem.addEventListener("click", e => this.mouseHandler(e)); } mouseHandler(e){ switch(e.type){ case "mouseenter": if(this.bool) return this.elem.style.backgroundColor = "#e6e6e6"; break; case "mouseleave": if(this.bool) return this.elem.style.backgroundColor = "#fff"; break; case "focusin": this.setState(DropDownMemu.DROPDOWN); this.bool = true; break; case "focusout": this.setState(DropDownMemu.DEFAULT); this.bool = false; case "click" : if(e.target.constructor !== HTMLLIElement) return this._name = e.target.textContent; // Modify the currently displayed content when clicked, reset the style, and throw events to inform the external world of the current content. this.setContent(); let evt = new FocusEvent("focusout"); this.elem.dispatchEvent(evt); } } set name(_name){ this._name = _name; this.setContent(); } get name(){ return this._name; } set list(_list){ this._list = _list; this.ul.innerHTML = ""; this.ul.appendChild(this.createLi()); } // Modify the currently displayed content of the menu and throw data setContent(_name){ this._name = _name || this._name; this.spanLabel.textContent = this._name; let evt = new MouseEvent("change"); if(!evt.data) evt.data = {} evt.data[this.label] = this._name; this.dispatchEvent(evt); } //Create a drop-down menu option based on the specified list. createLi(_list){ this._list = _list || this._list; let elem = document.createDocumentFragment(); this._list.forEach((item, index) => { let li = document.createElement("li"); li.textContent = item; Object.assign(li.style, { lineHeight:"26px", padding:"0 15px", }) elem.appendChild(li); }) return elem; } setState(type){ switch(type){ case DropDownMemu.DROPDOWN: this.elem.style.backgroundColor = "#e6e6e6"; this.ul.style.display = "block"; break; case DropDownMemu.DEFAULT: this.elem.style.backgroundColor = "#fff"; this.ul.style.display = "none"; break; } } appendTo(parent){ super.appendTo(parent); DropDownMemu.Obj[this.label] = this; } render() { this.elem.setAttribute("tabIndex",1); this.spanLabel = document.createElement("span"); this.spanCaret = document.createElement("span"); this.ul = document.createElement("ul"); this.elem.appendChild(this.ul); this.spanLabel.textContent = this._name; this.elem.appendChild(this.spanLabel); this.elem.appendChild(this.spanCaret); } setStyle() { Object.assign(this.elem.style, { float: "left", minHeight: "20px", minWidht: "80px", color: "#333", fontWeight: "normal", textAlign: "center", whiteSpace: "nowrap", verticalAlign: "middle", cursor: "pointer", border: "1px solid #ccc", borderRadius: "4px", backgroundColor: "#fff", padding: "6px 12px", fontSize: "14px", userSelect: "none", marginRight: "100px", position:"relative", }); Object.assign(this.spanLabel.style, { float: "left", padding: "0 5px" }) Object.assign(this.spanCaret.style, { display: "inline-block", verticalAlign: "middle", borderTop: "4px dashed", borderRight: "4px solid transparent", borderLeft: "4px solid transparent", }) Object.assign(this.ul.style, { listStyle: "none", position: "absolute", top: "100%", left: "0", zIndex: "1000", minWidth: "100px", padding: "5px 0px", margin: "2px 0 0", fontSize: "14px", textAlign: "left", backgroundColor: "#fff", border: "1px solid rgba(0, 0, 0, 0.15)", borderRadius: "4px", boxShadow: "0 6px 12px rgba(0, 0, 0, 0.175)", display: "none", }) } }
export default class Component extends EventTarget{ elem; constructor(_type){ super(); this.elem = this.createElem(_type); } createElem(_type){ let elem = document.createElement(_type); return elem; } appendTo(parent){ if(typeof parent==="string") parent = document.querySelector(parent); parent.appendChild(this.elem); } }
let http = require("http"); let querystring = require("querystring"); let data, req, res; // Read all city data and parse them into objects, and read them synchronously. let fs = require("fs"); let allCityInfo = JSON.parse(fs.readFileSync('./city.json')); let server = http.createServer(listenerHandler); server.listen(4010,"10.9.72.245",listenerDoneHandler); function listenerHandler(_req,_res){ req = _req; res = _res; res.writeHead(200,{ "content-type":"text/html;charset=utf-8", "Access-Control-Allow-Origin":"*", "Access-Control-Allow-Headers":"*", }); data=""; req.on("data",function(_data){ data=_data; }) req.on("end",receiveHandler); } function receiveHandler(){ // console.log(allCityInfo); // Parse the interface type according to the url of the request header let type = req.url.trim().split("?")[0].replace(///g,""); console.log(type); // Parse the incoming parameters according to the url of the request header if(req.method.toUpperCase()==="GET"){ if(req.url.includes("favicon.ico")) return res.end(); else data = req.url.includes("?") ? req.url.split("?")[1] : ""; } try{ data = JSON.parse(data); }catch{ data = querystring.parse(data); } console.log(data); //Find data based on interface type. let list = {}; switch(type){ case "getProvince": list.province = Object.keys(allCityInfo); break; case "getCity" : list.city = Object.keys(allCityInfo[data.province]); break; case "getCounty": list.county = allCityInfo[data.province][data.city]; break; } console.log(list); res.write(JSON.stringify(list)); res.end() } function listenerDoneHandler(){ console.log("Service started successfully"); }
{
"Beijing": {
"Beijing": ["Dongcheng District", "Xicheng District", "Chongwen District", "Xuanwu District", "Chaoyang District", "Fengtai District", "Shijingshan District", "Haidian District", "Mentougou District", "Fangshan District", "Tongzhou District", "Shunyi District", "Changping District", "Daxing District", "Pinggu District", "Huairou District", "Miyun County", "Yanqing County", "Others"]
} ,
"Tianjin": {
"Tianjin": ["Heping District", "Hedong District", "Hexi District", "Nankai District", "Hebei District", "Hongjiao District", "Binhai New District", "Dongli District", "Xiqing District" District", "Jinnan District", "Beichen District", "Ninghe District", "Wuqing District", "Jinghai County", "Baodi District", "Jixian", "Tanggu District", "Hangu District", " Dagang District", "Baodi District", "Others"]
},
}
The above is a detailed explanation of the three-level linkage menu using JS (with explanation of ideas). For more information, please pay attention to other related articles on the PHP Chinese website!