按父选择的索引值对选项子项进行排序(仅 Javascript)

Sort Option children by index value for both parent Selects (Javascript Only)

我正在尝试使用 仅使用香草 Javascript无 JQuery 或 JS 包,用于学习目的。

当用户双击一个选项时,该选项会附加到另一个父项 Select (boxB) 并从原始父项 Select (boxA) 中删除,反之亦然。 Option 元素具有数值递增的数据索引属性。如何按索引值维护选项的数字顺序,而不管它们附加的是哪个父项 Select?

<!-- HTML -->
<select id="ctst1" class="form-control form-control-sm" size="8" multiple>
    <option data-index="1" value="CPT">CPT</option>
    <option data-index="2" value="SST">SST</option>
    <option data-index="3" value="EDTA">EDTA</option>
    <option data-index="4" value="PAXgene">PAXgene</option>
    <option data-index="5" value="NP SWB">NP SWB</option>
    <option data-index="6" value="OP SWB">OP SWB</option>
    <option data-index="7" value="RCTL SWB">RCTL SWB</option>
    <option data-index="8" value="Self NS">Self NS</option>
</select>
<select id="ctst2" class="form-control form-control-sm" size="8" multiple></select>

// Javascript
const dataFrame = {
    "selCollTubeSampTypes": [
        ["CPT", "CPT"],
        ["SST", "SST"],
        ["EDTA", "EDTA"],
        ["PAXgene", "PAXgene"],
        ["NP SWB", "NP SWB"],
        ["OP SWB", "OP SWB"],
        ["RCTL SWB", "RCTL SWB"],
        ["Self NS", "Self NS"]
    ]
};

function dualListBox(qryId1, qryId2, dataset) {
    const boxA = document.getElementById(qryId1);
    const boxB = document.getElementById(qryId2);
    // Populate avail box with data
    for (let i = 0; i < dataset.length; i++) {
        const eleOption = document.createElement("option");
        eleOption.dataset.index = i + 1; // Adds data-index value to Option node
        if (dataset[i][0] === "") {
            eleOption.textContent = dataset[i][1];
        } else {
            eleOption.value = dataset[i][0];
            eleOption.textContent = dataset[i][1];
        };
        boxA.appendChild(eleOption);
    };
    // When option in Box A is double-clicked, move it to Box B
    boxA.addEventListener("dblclick", e => {
        let eTargetOpt = e.target.closest("option");
        if (!eTargetOpt) return;
        if (!boxA.contains(eTargetOpt)) return;
        if (eTargetOpt) {
            boxB.appendChild(eTargetOpt);
            boxB.selectedIndex = "-1";
            boxA.removeChild(eTargetOpt);
        };
        e.stopPropagation();
    }, false);
    // When option in Box B is double-clicked, move it to Box A
    boxB.addEventListener("dblclick", e => {
        let eTargetOpt = e.target.closest("option");
        if (!eTargetOpt) return;
        if (!boxB.contains(eTargetOpt)) return;
        if (eTargetOpt) {
            boxA.appendChild(eTargetOpt);
            boxA.selectedIndex = "-1";
            boxB.removeChild(eTargetOpt);
        };
        e.stopPropagation();
    }, false);

dualListBox("ctst1", "ctst2", dataFrame.selCollTubeSampTypes);

由于您基本上对两个 select 使用相同的逻辑,因此我合并为一个函数。我们可以使用单个 className 并依靠基本的 JS 来确定哪个是哪个,而不是使用可能有问题的 ID。排序机制 - 您最初的问题 - 通过将所有选项元素放入一个数组,按 dataset.index 对数组进行排序,然后重新填充 select 元素来工作。一组逻辑适用于两个盒子

const dataFrame = {
  "selCollTubeSampTypes": [
    ["CPT", "CPT"],
    ["SST", "SST"],
    ["EDTA", "EDTA"],
    ["PAXgene", "PAXgene"],
    ["NP SWB", "NP SWB"],
    ["OP SWB", "OP SWB"],
    ["RCTL SWB", "RCTL SWB"],
    ["Self NS", "Self NS"]
  ]
};

const dualListBox = (className, dataset) => {
  let boxSet = document.querySelectorAll(`.${className}`);
  dataset.forEach((item, i) => {
    let val = item[0] != "" ? item[0] : item[1];
    boxSet[0].innerHTML += `<option value="${val}" data-index="${i}">${item[1]}</option>`;
  })
  boxSet.forEach(myBox => myBox.addEventListener("dblclick", e => {
    let eTargetOpt = e.target.closest("option");
    if (!eTargetOpt) return;
    let targBox = myBox == boxSet[0] ? boxSet[1] : boxSet[0]
    if (eTargetOpt) {
      let targOptions = [...targBox.querySelectorAll('option')];
      targBox.innerHTML = '';
      myBox.removeChild(eTargetOpt);
      targOptions.push(eTargetOpt);
      targOptions.sort((a, b) => a.dataset.index - b.dataset.index)
      targOptions.forEach(opt => targBox.appendChild(opt))
    }
    e.stopPropagation();
  }, false));
}

dualListBox("box-set", dataFrame.selCollTubeSampTypes);
<select class="form-control form-control-sm box-set" size="8" multiple></select>
<select class="form-control form-control-sm box-set" size="8" multiple></select>