最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - How to add and remove elements dynamically JavaScript - Stack Overflow

programmeradmin4浏览0评论

I have this let's say quiz maker where I create questions. It looks like this:

The user can choose to display this rating system in 4 different ways:

  1. 1 2 3 4 5 -> 1-5 rating
  2. 1 2 3 4 5 6 7 8 9 10 -> 1-10 rating
  3. ⭐ ⭐ ⭐ ⭐ ⭐ -> 1-5 stars
  4. ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ -> 1-10 stars

The default rating system is from 1 to 5 numbers. But the user has the option to make it from 1 to 10 using the toggle switch as well as stars instead of numbers. My problem is that I don't know how to add 5 more numbers when the switch is turned on and then when it turned off remove them again, or remove the numbers and add stars when the other toggle switch is turned on.

I imagine that I need something like:

depending on the current state:
if (1-10_switch.isChanged){
   (addMore(stars || numbers) || remove ) 
}
else if (stars_switch.isChanged){
   (changeIconTo(stars || numbers)) 
}

I have the code to check if the switch goes on and off:

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

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

And this is the code where I add the numbers:

  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);
  }

Does anyone know what approach I can use to solve this? Or maybe someone has done it and know how to do? Thanks.

I have this let's say quiz maker where I create questions. It looks like this:

The user can choose to display this rating system in 4 different ways:

  1. 1 2 3 4 5 -> 1-5 rating
  2. 1 2 3 4 5 6 7 8 9 10 -> 1-10 rating
  3. ⭐ ⭐ ⭐ ⭐ ⭐ -> 1-5 stars
  4. ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ -> 1-10 stars

The default rating system is from 1 to 5 numbers. But the user has the option to make it from 1 to 10 using the toggle switch as well as stars instead of numbers. My problem is that I don't know how to add 5 more numbers when the switch is turned on and then when it turned off remove them again, or remove the numbers and add stars when the other toggle switch is turned on.

I imagine that I need something like:

depending on the current state:
if (1-10_switch.isChanged){
   (addMore(stars || numbers) || remove ) 
}
else if (stars_switch.isChanged){
   (changeIconTo(stars || numbers)) 
}

I have the code to check if the switch goes on and off:

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

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

And this is the code where I add the numbers:

  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);
  }

Does anyone know what approach I can use to solve this? Or maybe someone has done it and know how to do? Thanks.

Share Improve this question edited Jul 29, 2022 at 8:35 Chris Costa asked Jul 29, 2022 at 8:05 Chris CostaChris Costa 6911 gold badge6 silver badges19 bronze badges 20
  • Can we see HTML part, of the numbersDiv? – aca Commented Jul 29, 2022 at 8:08
  • 1 Why do these numbers need to get displayed at this point to begin with? The user that is creating the question here in this place, is not the one who will actually be rating it - no? If this is pure display at this point, and there is no more functionality attached to these numbers - then I would simply toggle a class on the parent element, that shows the second half ... – C3roe Commented Jul 29, 2022 at 8:08
  • @aca i added it but i don't think it matters that much – Chris Costa Commented Jul 29, 2022 at 8:10
  • @CBroe You are right, the user who is creating is not the one who would be rating. There is no functionality at this point. Only visual – Chris Costa Commented Jul 29, 2022 at 8:12
  • 1 It was an element not strictly bound to the question that confused me... but indeed you didn't mention it expicitely in your logic so it was fair enough. My bad. Anyway I know you may prefer to go with the strategy you already decided.. but seriously.. you might just have a sequence of elements for 1 to 10 and embed the segment 6-10 inside a container that will be toggled a css class having display:none. Wouldn't it be enough? no because you are asking how to create and remove.. ok. :) fiy I will add here the template option developer.mozilla/en-US/docs/Web/HTML/Element/template – Diego D Commented Jul 29, 2022 at 8:30
 |  Show 15 more ments

6 Answers 6

Reset to default 3

I would not generate that HTML dynamically. Create it upfront, and hide the last 5 elements with a CSS class. The numbers can be generated with a CSS counter at the start as well.

With this set up the code only needs to toggle CSS classes:

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>

You can take the logic of creating the numbers and enter it into a function:

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)

Then, he event handler receives the event itself as an argument, and in a case of a change event for HTML checkbox (I assume this is a checkbox due to the variable name), the event consists a checked property, so you can change the event listener:

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

Then create a removeNumbers method:

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

add new elements:

// 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 plete the processing you need to do for the child nodes

remove elements:

ul.removeChild(li)

try use this in your code ,Hope its could help you

A simple/straightforward way is to :

  1. Store the status of isTen and isStar, which are both default to false
  2. Create a toggle() function to change these status when the switch is clicked.
  3. Generate the content according to these two flag (i.e. 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>

This version uses a <form> and renders multiple questions.

Details are mented 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>

Here's my pletely dynamic and general solution. I came up with a dynamic solution because I'm assuming that you'll need to create multiple quizzes thus multiple rating selectors.

We can create a rating selector element with the createRatingSelector function and it returns a 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>

发布评论

评论列表(0)

  1. 暂无评论