如何在 JavaScript 中动态添加和删除元素

5
我有一个类似问答制作器的东西,用于创建问题。它看起来像这样:

enter image description here

用户可以选择以4种不同的方式显示此评级系统:
  1. 1 2 3 4 5 -> 1-5评分
  2. 1 2 3 4 5 6 7 8 9 10 -> 1-10评分
  3. ⭐ ⭐ ⭐ ⭐ ⭐ -> 1-5星级
  4. ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ -> 1-10星级
默认评级系统为1到5个数字。但是用户可以使用切换开关将其更改为1到10,同时使用星号而不是数字。我的问题是,我不知道如何在打开开关时添加5个数字,然后再关闭时将它们删除,或者在打开另一个切换开关时删除数字并添加星号。
我想象中需要类似以下的东西:
depending on the current state:
if (1-10_switch.isChanged){
   (addMore(stars || numbers) || remove ) 
}
else if (stars_switch.isChanged){
   (changeIconTo(stars || numbers)) 
}

我有检查开关是否打开和关闭的代码:
oneToTenButtonCheckbox.addEventListener("change", function () {
    OneToTen = OneToTen ? false : true;
});

starsButtonCheckbox.addEventListener("change", function () {
    isStars = isStars ? false : true;
});

这是我添加数字的代码:

  var numbersDiv = document.createElement("div");  
  numbersDiv.className = "row";
  for (let i = 0; i < 5; i++) {
    var num = document.createElement("div");
    num.insertAdjacentText("beforeend", i + 1);
    if (i == 0) num.className = "col-1 offset-1 mb-2";
    else num.className = "col-1 mb-2";
    numbersDiv.appendChild(num);
  }

有人知道我可以使用什么方法来解决这个问题吗?或者可能有人已经做过并且知道如何做吗?谢谢。


1
为什么这些数字需要在此时显示呢?在这里创建问题的用户并不是实际评分的人,对吧?如果这只是纯粹的“显示”,并且这些数字没有更多的功能附加 - 那么我会简单地在父元素上切换一个类,显示第二部分... - CBroe
我明白你们的意思。虽然下一步是让用户在星号和数字之间进行选择。所以如果我现在显示和隐藏,它不起作用,因为数字Div的位置已经被占据了,如果我按照这种方法进行操作,星号Div将必须放置在隐藏的数字Div下面。我想做的是例如删除数字,在那些数字的位置上添加星号。 - Chris Costa
它会起作用。您只需通过有效更改两者的可见性(它们重叠在一起),交换一个div(比如数字div)与另一个div(星星div)。如果您想继续使用自己的方法,这里有一个简单的示例。 - takecare
我不确定我是否正确理解了..但根据我的理解,您有两个切换开关控制着同一件事情。因此,当您切换1-10的值时,它应该自动触发Stars的切换。这种UI方式非常令人困惑,而实际上可以是相同的UI控件具有两个选项。显示/隐藏1-10 / 1-5的逻辑就像有两个HTML块,您只需切换可见/隐藏即可。创建和删除只是浪费时间和复杂性。而且它得到了4个赞..有时候这对我来说是个谜。 - Diego D
1
有一个元素并不严格绑定于问题,这让我感到困惑...但是你确实没有在逻辑中明确提到它,所以也算公平。我的错。无论如何,我知道你可能更喜欢采用你已经决定的策略...但是说真的...你可以只有1到10个元素的序列,并将6-10段嵌入一个容器中,该容器将切换为display:none的css类。这样就足够了吗?不行,因为你正在问如何创建和删除...好的。:) 顺便提一下,我会在这里添加模板选项 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template - Diego D
显示剩余15条评论
6个回答

3
我不会动态生成该HTML。预先创建它,并使用CSS类隐藏最后5个元素。数字也可以在开头用CSS计数器生成。
有了这种设置,代码只需要切换CSS类即可:

const answer = document.querySelector("#answer");
document.querySelector("#oneToTenButtonCheckbox").addEventListener("change", () => {
    answer.children[1].classList.toggle("hidden");
});
document.querySelector("#starsButtonCheckbox").addEventListener("change", () => {
    answer.classList.toggle("stars");
});
body {
    counter-reset: star-num;
}
#answer.stars > span > span::after {
    content: "⭐";
}
#answer > span > span::after {
    counter-increment: star-num;
    content: counter(star-num);
}
#answer > span > span {
    display: inline-block;
    text-align: center;
    width: 30px;
}
.hidden {
    display: none
}
<label><input type=checkbox id="oneToTenButtonCheckbox">1 - 10</label><br>
<label><input type=checkbox id="starsButtonCheckbox">Stars</label><br>
 
<div id="answer">
    <span><span></span><span></span><span></span><span></span><span></span></span><span class="hidden"><span></span><span></span><span></span><span></span><span></span></span>
</div>


1
你可以将创建数字的逻辑放入一个函数中:
```

您可以将创建数字的逻辑放入一个函数中:

```
var numbersDiv = document.createElement("div");  
numbersDiv.className = "row";

function createNumbers(startNumber, endNumber)
  for (let i = startingNumber; i <= endNumber; i++) {
    var num = document.createElement("div");
    // add id so you can delete later
    num.id = "ratingNumber" + i;
    num.insertAdjacentText("beforeend", i);
    if (i == 1) num.className = "col-1 offset-1 mb-2";
    else num.className = "col-1 mb-2";
    numbersDiv.appendChild(num);
  }
}

//initial call to create 1-5
createNumbers(1, 5)

然后,事件处理程序将事件本身作为参数接收,并且在HTML复选框的更改事件的情况下(我假设这是由于变量名称的复选框),事件包含一个checked属性,因此您可以更改事件侦听器:

oneToTenButtonCheckbox.addEventListener("change", function (event) {
    event.checked ? createNumbers(6, 10) : removeNumbers(6, 10); 
});

接下来创建一个 removeNumbers 方法:

function removeNumbers(startNumber, endNumber) {
    for (let i = startNumber; i <= endNumber; i++) {
        const numToRemove = document.getElementById("ratingNumber" + i);
        numToRemove.remove()
    }
}

1

添加新元素:

// first: make a new element
let li = document.createElement('li')
// second:add new element to Parent Node(such as ul)
//Insert at the end of the parent element
ul.appendChild(li)
//Insert into the specified position of the parent element
ul.insertBefore(li, ul.children[0])
//Please note that before you add the DOM child nodes (step 2) please complete the processing you need to do for the child nodes

删除元素:

ul.removeChild(li)

尝试在您的代码中使用此代码,希望它能够帮助您。


1

一个简单/直接的方法是:

  1. 存储 isTenisStar 的状态,它们都默认为 false
  2. 创建一个 toggle() 函数,在开关被点击时改变这些状态。
  3. 根据这两个标志生成内容(即 display())。

isTen = false;
isStar = false;
display()

function toggle(flag){
  /* toggle the corresponding flag */
  if (flag == "isTen"){
    isTen = !isTen;
  }else{
    isStar = !isStar;
  }
  /* generate display with latest flag */
  display()
}

function display(){
  /* generate the output string with isTen and isStar flag*/
  n = isTen ? 10 : 5;
  output = "";
  for (let i = 0; i < n; i++) {
      if (isStar){
         output += "⭐";
      } else {
         output += i+1;
      }
  }
  /* clear the display span first*/
  document.getElementById("display").innerHTML = "";
  /* show output string on display span */
  document.getElementById("display").innerHTML = output;
  showStatus();
}

function showStatus(){
  document.getElementById("status").innerHTML = "";
  document.getElementById("status").innerHTML = "isTen : "+isTen+"<br>isStar :"+isStar;
}
<label>
  <input type="checkbox" onclick="toggle('isTen')">
  isTen
</label>
<br>
<label>
  <input type="checkbox" onclick="toggle('isStar')">
  isStar
</label>
<br>
<span id="display"></span>
<br><br><br>
<span id="status"></span>


1
这是我的完全动态和通用解决方案。我想出了一种动态解决方案,因为我假设您需要创建多个测验,因此需要多个评分选择器。
我们可以使用createRatingSelector函数创建一个评分选择器元素,并返回一个HTMLDivElement

const stateElement = document.getElementById("state");

const quizRatingSelector = createRatingSelector({
  altRatingScale: 10,
  defaultRatingScale: 5,
  altRatingType: "star",
  defaultRatingType: "number",
  altRatingTypeLabel: "Star",
  defaultRatingTypeLabel: "Number Scale",

  // listen to change event
  onChange: ({ratingScale, ratingType}) => {
    const stateString = `scale: ${ratingScale}, type: ${ratingType}`;
    stateElement.innerText = stateString;
  },
});

document.body.appendChild(quizRatingSelector);

// we can get the current state as
// console.log(quizRatingSelector.getState());

// ------------------------------------------

// returns a <div> element
// see the argument destructuring below to understand function API
function createRatingSelector(arg = {}) {
  const {
    altRatingType,
    altRatingScale,
    defaultRatingType,
    altRatingTypeLabel,
    defaultRatingScale,

    // this function will be called whenever the rating scale or type changes
    onChange = () => {},

    defaultRatingTypeLabel,
    NON_NUMBER_RATING_CHAR = "⭐",
    parentClassName = "rating-selector",
    typeSwitchClassName = "rating-type-switch",
    scaleSwitchClassName = "rating-scale-switch",
    outputElementClassName = "rating-selector-output",
  } = arg;

  const ratingSelector = document.createElement("div");
  ratingSelector.setAttribute("class", parentClassName);

  // we'll store the ratingType and ratingScale in the dataset of this selector
  // in case you need it.
  ratingSelector.dataset.ratingType = defaultRatingType;
  ratingSelector.dataset.ratingScale = defaultRatingScale;

  ratingSelector.getState = function () {
    return {
      ratingType: ratingSelector.dataset.ratingType,
      ratingScale: ratingSelector.dataset.ratingScale,
    };
  };

  const ratingOutput = document.createElement("div");
  ratingOutput.setAttribute("class", outputElementClassName);

  // this function is needed in the onClick function of switches
  const showResultLocal = () =>
    showResult({
      outputElement: ratingOutput,
      nonNumberValue: NON_NUMBER_RATING_CHAR,
      ratingType: ratingSelector.dataset.ratingType,
      ratingScale: ratingSelector.dataset.ratingScale,
    });

  const ratingScaleSwitch = createSwitch({
    name: "rating-scale",
    onValue: altRatingScale,
    offValue: defaultRatingScale,
    className: scaleSwitchClassName,
    onLabel: `Rating Scale: 1 - ${altRatingScale}`,
    offLabel: `Rating Scale: 1 - ${defaultRatingScale}`,

    onClick: ({ value }) => {
      ratingScale = value;
      ratingSelector.dataset.ratingScale = value;

      showResultLocal();
      onChange(ratingSelector.getState());
    },
  });

  const ratingTypeSwitch = createSwitch({
    name: "rating-type",
    className: typeSwitchClassName,

    onValue: altRatingType,
    offValue: defaultRatingType,

    onLabel: `Rating type: ${altRatingTypeLabel}`,
    offLabel: `Rating type: ${defaultRatingTypeLabel}`,

    onClick: ({ value }) => {
      ratingSelector.dataset.ratingType = value;
      showResultLocal();

      onChange(ratingSelector.getState());
    },
  });

  ratingSelector.appendChild(ratingScaleSwitch);
  ratingSelector.appendChild(ratingTypeSwitch);
  ratingSelector.appendChild(ratingOutput);

  showResultLocal();

  return ratingSelector;
}

/**
 * Creates a __input__ and __label__ element and wraps it with a <div>
 * e.g.,
 * <div>
 *  <label>The Label</label>
 *  <input type="checkbox" ...other attributes... />
 * </div>
 *
 * see the argument destructuring below to understand function API
 * */
function createSwitch(arg = {}) {
  const {
    onLabel,
    offLabel,

    onValue,
    offValue,

    id = "",
    name = "",
    className = "",

    onClick = () => {},
  } = arg;

  const switchName = name;
  const switchAttributes = {
    id,
    name: switchName,
    class: className,
    type: "checkbox",
  };

  const toggleSwitch = document.createElement("input");

  for (const [name, value] of Object.entries(switchAttributes))
    toggleSwitch.setAttribute(name, value);

  const switchLabel = document.createElement("label");
  switchLabel.setAttribute("for", switchName);
  switchLabel.innerText = offLabel;

  // click event handling
  toggleSwitch.addEventListener("click", () => {
    switchLabel.innerText = toggleSwitch.checked ? onLabel : offLabel;

    onClick({
      id,
      name: switchName,
      active: toggleSwitch.checked,
      value: toggleSwitch.checked ? onValue : offValue,
    });
  });

  const switchWrapper = document.createElement("div");
  switchWrapper.appendChild(toggleSwitch);
  switchWrapper.appendChild(switchLabel);

  return switchWrapper;
}


// see the argument destructuring below to understand function API
function showResult(arg = {}) {
  const { outputElement, ratingScale, ratingType, nonNumberValue } = arg;

  while (outputElement.childNodes.length > ratingScale)
    outputElement.removeChild(outputElement.childNodes[0]);
  while (outputElement.childNodes.length < ratingScale)
    outputElement.appendChild(document.createElement("span"));

  outputElement.childNodes.forEach((child, index) => {
    child.innerText = ratingType === "number" ? index + 1 : nonNumberValue;
  });
}
.rating-selector-output > span {
  width: 2ch;
  text-align: center;
  display: inline-block;
}
<body>
<p id="state">State...</p>
<hr>
</body>


1

这个版本使用了一个 <form> 并渲染多个问题。

Details are commented in example

// Reference <form>
const form = document.forms.interface;

/**
 * @desc Renders the HTML of each question up to the given number
 * @param {number} items - Number of questions to render
 */
function addQuestions(items) {
  const qar = form.qar;
  for (let i = 0; i < items; i++) {
    let q = `
    <li class='item${i + 1}'>
      <input id='que${i + 1}' name='que' class='que' type='text' placeholder='Enter question'> 
      <label style='display: inline-block'>Modify 
        <input name='mod' type='radio' value='${i + 1}'>
      </label>
      <fieldset id='row${i + 1}' name='row' class='row'>
        <a></a><a></a><a></a><a></a><a></a>
        <a class='extra hide'></a><a class='extra hide'></a>
        <a class='extra hide'></a><a class='extra hide'></a>
        <a class='extra hide'></a>
      </fieldset>
    </li>`;
    qar.insertAdjacentHTML('beforeend', q);
    const item = document.querySelector(`.item${i + 1}`);
    [...item.querySelectorAll('a')].forEach((a, idx) => {
      a.textContent = idx + 1;
    });
  }
  form.elements.mod[0].checked = true;
}

addQuestions(3);

// Bind change event to <form>
form.onchange = modify;

// This modifies the question that has the checked mod radio button
function modify(e) {
  // Reference the checkbox the user clicked
  const clk = e.target;
  // Reference all form controls
  const fc = this.elements;
  // Get the value of the mod radio button group
  const mod = fc.mod.value;
  // Reference <input> and <fieldset>
  const que = document.getElementById('que' + mod);
  const row = document.getElementById('row' + mod);
  // If the user clicked the required checkbox...
  if (clk.id === 'req') {
    //...toggle the required attribute to the selected <input>
    que.toggleAttribute('required');
  }
  // If the user clicked the 10 checkbox...
  if (clk.id === 'qty') {
    // ...toggle the selected row 5 a.extra .hide and .show classes 
    row.querySelectorAll('.extra').forEach(ex => {
      ex.classList.toggle('hide');
      ex.classList.toggle('show');
    });
  }
  // If the user clicked the stars checkbox...
  if (clk.id === 'str') {
    // ...toggle the .stars class of row
    row.classList.toggle('stars');
  }
  // Reset the checkboxes immediately
  Array.from(fc.panel.elements).forEach(chx => chx.checked = false);
}
input {
  font: inherit;
  display: inline-block;
}

#panel,
label {
  display: flex;
  justify-content: space-around;
  align-items: center;
  width: max-content;
  padding: 5px;
}

#qar {
  list-style-type: decimal;
  border: 0;
}

.que {
  margin: 8px 0;
}

.row {
  display: flex;
  flex-flow: row nowrap;
  justify-content: space-evenly;
  align-items: center;
  border: 0;
}

.row.stars a {
  color: transparent;
}

.row.stars a::before {
  content: '⭐';
  color: black;
}

.hide {
  display: none;
}

.show {
  display: block;
}

[required] {
  outline: red 1px solid;
}
<form id='interface'>
  <fieldset>
    <legend>Q &amp; A/Rating</legend>
    <fieldset id='panel'>
      <label>Required <input id='req' type='checkbox'></label>
      <label>10 <input id='qty' type='checkbox'></label>
      <label>Stars <input id='str' type='checkbox'></label>
    </fieldset>
    <fieldset id='qar'>

    </fieldset>
  </fieldset>
</form>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接