樹形結構的checkbox


如上圖, 圖中元素都為type='checkbox'的input
input1選中, 則下面所有input都要選中, input2選中, 下面的input4input5也都要選中, input2input3都選中, 同樣input1也要選中,
這樣用js怎么做呢?

? 最佳回答:

在線體驗地址

const container = document.createElement("div");
const treeNodeTemplate = (key, label) => `<div id="${key}">
              <label>${label}</label>
              <input type="checkbox" />
              <ul class="children"></ul>
            </div>
            `;
const createTreeNodeFromKeyAndLabel = (key, label) => {
  const tmeplate = treeNodeTemplate(key, label);
  container.innerHTML = tmeplate;

  return container.children[0];
};

const effects = [];
const visited = new Set();
let flushed = false;

class TreeNode {
  elem = null;
  parent = null;
  children = [];
  data = null;
  effects = [];

  constructor(data) {
    this.data = data;
    const uninitialized = this.render(data);
    this.initEvent(uninitialized);
  }

  setCheckboxValue(node, val) {
    const checkbox = this.getCheckboxFromTreeElem(node.elem);
    node.data.checked = val;
    checkbox.checked = val;
  }

  setChildrenValueRecursively(newVal, needTrackSideEffect = false) {
    for (const child of this.children) {
      const elem = child.elem;

      this.setCheckboxValue(child, newVal);
      if (needTrackSideEffect) {
        visited.add(child);
      }
      child.setChildrenValueRecursively(newVal);
    }
  }

  getCheckboxFromTreeElem(elem) {
    return elem.children[1];
  }

  bubbleProperties(newVal) {
    if (!this.parent) return;

    if (newVal) {
      const needUpdate = this.parent.children.every((v) => {
        const checkbox = this.getCheckboxFromTreeElem(v.elem);

        return checkbox.checked;
      });

      if (needUpdate) {
        this.setCheckboxValue(this.parent, newVal);

        this.parent.bubbleProperties(newVal);
      }
    } else {
      this.setCheckboxValue(this.parent, newVal);
      this.parent.bubbleProperties(newVal);
    }
  }

  initEvent(uninitialized) {
    console.assert(!!this.elem, this);

    if (!uninitialized) return;

    const checkbox = this.getCheckboxFromTreeElem(this.elem);
    console.assert(checkbox.tagName === "INPUT", checkbox);

    checkbox.addEventListener("change", (e) => {
      if (e.target.checked) {
        //該節點被切換為true需要將他所有子節點全部置為true
        //并且如果他所在層級所在的節點全部都為true還需要向上冒泡

        //把子節點變為true
        this.setChildrenValueRecursively(true);

        //向上冒泡
        this.bubbleProperties(true);
      } else {
        //把子節點全部變為false
        this.setChildrenValueRecursively(false);

        //向上冒泡
        this.bubbleProperties(false);
      }
      this.data.checked = e.target.checked;
    });
  }

  render(data) {
    let elem = data.__elem;
    const { key, label, children, checked } = data;
    console.assert(!!(key && label), { key, label });
    let uninitialized = elem == null;

    if (checked === true) {
      effects.push(this);
    }

    if (!elem) {
      elem = createTreeNodeFromKeyAndLabel(key, label);
    }

    const childrenContainer = elem.querySelector(".children");

    data.__elem = elem;
    this.elem = elem;

    if (!children) return uninitialized;

    console.assert(Array.isArray(children), children);
    console.assert(!!childrenContainer, childrenContainer);

    for (const child of children) {
      const subTreeNode = new TreeNode(child);
      this.children.push(subTreeNode);
      subTreeNode.parent = this;
      childrenContainer.appendChild(subTreeNode.elem);
    }

    return uninitialized;
  }
}

const treeData = {
  key: "1",
  label: "1",
  children: [
    { key: "2", label: "2", children: [] },
    {
      key: "3",
      label: "3",
      checked: true,
      children: [
        { key: "5", label: "5" },
        { key: "6", label: "6" }
      ]
    },
    { key: "4", label: "4" },
    { key: "7", label: "7", checked: true }
  ]
};

/**
 * 將初始化抽離出來,以防止O(N^2)時間復雜度的初始化更新
 **/
const flushEffects = () => {
  for (let i = 0; i < effects.length; ++i) {
    const effect = effects[i];
    if (visited.has(effect)) continue;
    effect.setCheckboxValue(effect, true);
    visited.add(effect);
    effect.setChildrenValueRecursively(true, true);
  }

  effects.length = 0;
  visited.clear();
};

const renderTree = (data) => {
  const root = new TreeNode(data);
  flushEffects();
  return root;
};

const root = renderTree(treeData);
const app = document.getElementById("app");
app.appendChild(root.elem);
const button = document.getElementById("button");
button.onclick = () => {
  console.log(root.data);
};
主站蜘蛛池模板: 午夜DV内射一区区| 中文字幕一区精品| 日韩高清一区二区| 免费av一区二区三区| 日韩a无吗一区二区三区| 香蕉免费一区二区三区| 亚洲国产精品一区二区九九| 国产欧美色一区二区三区| 麻豆一区二区在我观看| 亚洲一区二区三区在线播放| 国产麻豆精品一区二区三区| 色狠狠色狠狠综合一区| 亚洲一区二区三区首页| 亚洲国产激情一区二区三区 | 亚洲香蕉久久一区二区| 在线观看国产一区| 国产精品第一区第27页| 久久精品国产一区二区三区| 一区二区三区在线免费| 日亚毛片免费乱码不卡一区| 精品一区二区三区视频| 亚洲欧洲一区二区三区| 一区二区3区免费视频| 亚洲国产综合无码一区二区二三区| 亚洲天堂一区在线| 久久青草国产精品一区| 精品一区二区三区在线成人| 无码精品人妻一区二区三区人妻斩| 一区二区三区无码视频免费福利 | 一区二区三区免费精品视频 | 亚洲AV成人精品一区二区三区 | 国产vr一区二区在线观看| 亚洲一区二区视频在线观看| 日本精品高清一区二区| 国产一区二区三区乱码| 3D动漫精品啪啪一区二区下载| 国产福利91精品一区二区三区| 日韩一区二区三区四区不卡| 国产伦精品一区二区三区免.费 | 蜜臀Av午夜一区二区三区| 无码日韩人妻av一区免费|