使用原生JS封装菜单组件实现省市县三级级联式菜单,并使用ajax和NodeJs实现前后端通信

数据格式如下:

{
	河北: {
		石家庄: ["长安区","桥东区","桥西区","新华区","井陉矿区","裕华区","井陉县","正定县","栾城县","行唐县","灵寿县","高邑县","深泽县","赞皇县","无极县","平山县","元氏县","赵县","辛集市","藁城市","晋州市","新乐市","鹿泉市","其他",],
		...
	},
	...
}

组件JS代码:

export default class List extends EventTarget {
  elem;
  btn;
  ul;
  _list;
  name;
  data;
  callback;

  displayBool = false;

  static prev;

  constructor(_list, _name, _callback) {
    super();
    this._list = _list;
    this.name = _name;
    this.callback = _callback;

    this.elem = this.createElem();
  }

  // 创建当前实例化对象对应的元素
  createElem() {
    if (this.elem) return this.elem;
    let div = document.createElement("div");
    Object.assign(div.style, {
      position: "relative",
      float: "left",
      paddingRight: "15px",
      paddingLeft: "15px",
    });

    this.createBtn(div);
    this.createUl(div);

    this.btn.addEventListener("mouseover", (e) => this.mouseoverHandler(e));
    this.ul.addEventListener("mouseover", (e) => this.mouseoverHandler(e));
    this.btn.addEventListener("mouseout", (e) => this.mouseoutHandler(e));
    this.ul.addEventListener("mouseout", (e) => this.mouseoutHandler(e));
    this.btn.addEventListener("click", (e) => this.clickHandler(e));
    this.ul.addEventListener("click", (e) => this.ulClickHandler(e));
    document.addEventListener("click", (e) => this.clickHandler(e));

    return div;
  }

  // 将当前实例化对象对应的元素添加到parent中
  appendTo(parent) {
    if (typeof parent === "string") parent = document.querySelector(parent);
    parent.appendChild(this.elem);
  }

  // 创建按钮
  createBtn(parent) {
    this.btn = document.createElement("button");

    Object.assign(this.btn.style, {
      color: "#333",
      backgroundColor: "#fff",
      border: "1px solid #ccc",
      borderRadius: "4px",
      textAlign: "center",
      cursor: "pointer",
      padding: "6px 12px",
      fontSize: "14px",
      userSelect: "none",
    });

    this.btn.innerHTML = `
    <span></span>
    <span></span>
    `;
    Object.assign(this.btn.children[1].style, {
      display: "inline-block",
      width: 0,
      height: 0,
      verticalAlign: "middle",
      borderTop: "4px dashed",
      borderRight: "4px solid transparent",
      borderLeft: "4px solid transparent",
    });

    parent.appendChild(this.btn);
  }

  // 创建列表容器及内容
  createUl(parent) {
    this.ul = document.createElement("ul");
    Object.assign(this.ul.style, {
      position: "absolute",
      top: "100%",
      left: 0,
      zIndex: 1000,
      display: this.displayBool ? "block" : "none",
      float: "left",
      minWidth: "160px",
      padding: "5px 0",
      margin: "2px 0 0",
      fontSize: "14px",
      textAlign: "left",
      listStyle: "none",
      border: "1px solid rgba(0, 0, 0, 0.15)",
      borderRadius: "4px",
      boxShadow: "0 6px 12px rgba(0, 0, 0, 0.175)",
    });

    // 设置列表内容
    this.list = this._list;

    parent.appendChild(this.ul);
  }

  // 设置列表内容
  set list(_list) {
    // 设置按钮显示的内容
    this.data = _list[0];
    this.btn.firstElementChild.textContent = this.data;

    // 创建列表中的内容
    var str = "";
    _list.forEach((item) => {
      str += `<li><a>${item}</a></li>`;
    });
    this.ul.innerHTML = str;

    // 设置ul下所有li中a标签的样式
    Array.from(this.ul.children).forEach((item) => {
      Object.assign(item.children[0].style, {
        display: "block",
        padding: "3px 20px",
        clear: "both",
        color: "#333333",
        textDecoration: "none",
        cursor: "pointer",
      });
    });
  }

  get list() {
    return this._list;
  }

  // 鼠标滑过按钮和列表中的a标签时,改变其样式
  mouseoverHandler(e) {
    if (e.currentTarget === this.btn) {
      Object.assign(e.currentTarget.style, {
        backgroundColor: "#e6e6e6",
        borderColor: "#adadad",
      });
    } else if (e.target.constructor === HTMLAnchorElement) {
      Object.assign(e.target.style, {
        backgroundColor: "#e6e6e6",
      });
    }
  }

  // 鼠标滑出按钮和列表中的a标签时,改变其样式
  mouseoutHandler(e) {
    var target = e.currentTarget === this.btn ? e.currentTarget : e.target;

    target.style.backgroundColor = "#fff";
    target.style.borderColor = "#ccc";
  }

  // 点击按钮切换当前列表的显示状态并隐藏其他列表
  // 点击document隐藏所有列表
  clickHandler(e) {
    if (e.currentTarget === this.btn) {
      e.stopPropagation();
      if (List.prev) {
        List.prev.displayBool = false;
        List.prev.elem.lastElementChild.style.display = "none";
      }
      List.prev = this;
      List.prev.displayBool = !List.prev.displayBool;
    } else if (e.currentTarget === document) this.displayBool = false;

    this.ul.style.display = this.displayBool ? "block" : "none";
  }

  // 点击列表中的内容时,将对应按钮的内容替换为被点击的内容,并执行回调函数callback
  ulClickHandler(e) {
    if (e.target.constructor !== HTMLAnchorElement) return;

    this.data = e.target.textContent;
    this.btn.firstElementChild.textContent = this.data;

    if (this.callback) this.callback(this.name);
  }
}

HTML代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>省市县三级级联式菜单</title>
  </head>
  <body>
    <script type="module">
      import List from "./List.js";

      var province, city, county;

      // 封装ajax请求函数
      function ajax(type, data) {
        if (data === undefined) data = "";

        var xhr = new XMLHttpRequest();
        xhr.addEventListener("load", loadHandler);
        xhr.open("GET", "http://10.9.72.238:4001/" + type + "/?" + data);
        xhr.send();
      }

      // ajax请求成功的回调函数
      function loadHandler(e) {
        var xhr = e.currentTarget;
        xhr.removeEventListener("load", loadHandler);

        // 获取发送ajax时的接口类型
        var type = xhr.responseURL.trim().split("?")[0];
        if (type.slice(-1) === "/") type = type.slice(0, -1);
        type = type.split("/").pop();

        // 获取返回的数据
        var res = JSON.parse(xhr.response);

        // 根据接口类型进行相应的操作
        switch (type) {
          case "init":
            province = new List(res.provinceList, "province", clickItem);
            city = new List(res.cities, "city", clickItem);
            county = new List(res.counties);
            province.appendTo("body");
            city.appendTo("body");
            county.appendTo("body");
            break;
          case "province":
            city.list = res.cities;
          case "city":
            county.list = res.counties;
            break;
        }
      }

      // 初始化
      ajax("init");

      // 切换省份和城市时发送不同的ajax请求
      function clickItem(name) {
        if (name === "province") {
          ajax("province", "provinceName=" + province.data);
        } else if (name === "city") {
          ajax(
            "city",
            "provinceName=" + province.data + "&cityName=" + city.data
          );
        }
      }
    </script>
  </body>
</html>

NodeJs代码:

var http = require("http");
var querystring = require("querystring");

var server = http.createServer(function (req, res) {
  var data = "";
  // 设置响应头
  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;
  });

  // 侦听end事件,在数据接收完后进行数据操作
  req.on("end", function () {
    // 获取请求数据的接口类型
    var type = req.url.trim().split("?")[0].replace(/\//g, "");

    // 如果请求方式是get,获取请求数据data
    if (req.method.toLowerCase() === "get") {
      if (req.url.includes("favicon.ico")) return res.end();
      data = req.url.includes("?") ? req.url.split("?")[1] : "";
    }

    // 将data转换为对象
    try {
      data = JSON.parse(data);
    } catch (e) {
      data = data ? querystring.parse(data) : {};
    }

    var o = {};
    // 根据接口类型进行相应数据操作
    switch (type) {
      case "init":
        var provinceList = Object.keys(dataList);
        o.provinceList = provinceList;
        var cities = Object.keys(dataList[provinceList[0]]);
        o.cities = cities;
        o.counties = dataList[provinceList[0]][cities[0]];
        break;
      case "province":
        var provinceName = data.provinceName;
        var cities = Object.keys(dataList[provinceName]);
        o.cities = cities;
        o.counties = dataList[provinceName][cities[0]];
        break;
      case "city":
        var provinceName = data.provinceName;
        var cityName = data.cityName;
        o.counties = dataList[provinceName][cityName];
        break;
    }

    // 返回操作完成后的数据,并结束响应
    res.write(JSON.stringify(o));
    res.end();
  });
});

server.listen(4001, "10.9.72.238", function () {
  console.log("服务启动成功");
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值