0

Here's how it's supposed to work. By default there are two sets of input fields where the percentage fields have a value of 50% each. Now whenever a new input field is added, the percentage field value should be divided between all of them, for example, if there are 3 sets then percentage fields should have 33.33% each. If there are 4 then 25% each.

The issue right now is whenever I add a new field, percentage is not divided the first time but then I add another field and that is when it is divided among all the fields except the new one.

Another issue is, if I delete one then the percentage doesn't reduce. It stays the same.

Please note that in the javascript code, I had to declare percInput variable twice, once outside the click event and once inside the click event. This is because the default fields must have 50% allocated to each before adding new fields. And this is the only way I could allocate it dynamically, although I could hardcode it in html.

Here's what I tried so far:

const form = document.querySelector("form");
const span = document.querySelector("span");
const addBtn = document.querySelector("#addField");
const html = `
    <div class="url-pair">
        <input type="url" placeholder="3">
        <input type="text" class="perc">
        <button class="delete-btn" type="button">Delete</button>
    </div>
`;

let percInput = document.querySelectorAll('.urls-container .perc');
let percentage = (100 / percInput.length).toFixed(2).toString() + "%";

percInput.forEach((item) => {
    item.setAttribute("value", percentage);
});

const removeField = (e) => {
    if (!e.target.classList.contains('delete-btn')) return;
    e.target.parentElement.remove();
}

// delete field
form.addEventListener('click', removeField);

// add field and divide input value automatically
addBtn.addEventListener('click', () => {

    percInput = document.querySelectorAll('.urls-container .perc');
    percentage = (100 / percInput.length).toFixed(2).toString() + "%";

    percInput.forEach((item) => {
        item.setAttribute("value", percentage);
    });

    span.insertAdjacentHTML("beforeBegin", html);

});
<form action="#" method="POST">

  <div class="urls-container">
    <div class="url-pair">
      <input type="url" placeholder="1">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <div class="url-pair">
      <input type="url" placeholder="2">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <span></span>
  </div>

  <div>
    <div>
      <button type="button" id="addField">Add Field</button>
    </div>

    <div>
      <button type="submit">Create</button>
    </div>
  </div>

</form>

4
  • 1
    You need to calculate the percentage after you have inserted the new line. Commented Jul 9, 2020 at 20:24
  • Oh yes. I understand now. But what about the percentage values after deleting input fields? Commented Jul 9, 2020 at 20:28
  • 1
    I would like just suggest to make the code cleaner, you repeat yourself there.. Commented Jul 9, 2020 at 20:45
  • Yes. I'll make a function as suggested by the answers below. Thanks for the heads up mate. Commented Jul 9, 2020 at 20:51

3 Answers 3

2

The problem is that in your addBtn.addEventListener you are calculating the percentage before you insert the new input into the document and so when you query the document for all the input elements, you miss the one you should have already added. If you simply move that code to after you insert it, it will work.

And, since you need the percentages updated when adding and removing elements, the code responsible for that should be broken out into its own function so it can be called after adding or removing fields.

const form = document.querySelector("form");
const span = document.querySelector("span");

// `.getElementById() is the fastest way to get an element
// reference when there is an ID. It's faster than .querySelector()
const addBtn = document.getElementById("addField");
const html = `
    <div class="url-pair">
        <input type="url" placeholder="3">
        <input type="text" class="perc">
        <button class="delete-btn" type="button">Delete</button>
    </div>
`;

let percInput = document.querySelectorAll('.urls-container .perc');
let percentage = (100 / percInput.length).toFixed(2).toString() + "%";

percInput.forEach((item) => {
    item.setAttribute("value", percentage);
});

const removeField = (e) => {
    if (!e.target.classList.contains('delete-btn')) return;
    e.target.parentElement.remove();
    
    // Now, call the code that updates the percentages
    updatePercentages();
}

// delete field
form.addEventListener('click', removeField);

// add field and divide input value automatically
addBtn.addEventListener('click', () => {
    // Insert the new element into the document first.
    span.insertAdjacentHTML("beforeBegin", html);
    
    // Now, call the code that updates the percentages
    updatePercentages();
});

function updatePercentages(){
    // This code only runs AFTER input elements have been added
    // or removed from the document.
    
    // Calculate your percentages based on what's in the document
    percInput = document.querySelectorAll('.urls-container .perc');
    percentage = (100 / percInput.length).toFixed(2).toString() + "%";
    
    // Then update the elements.
    percInput.forEach((item) => {
      item.setAttribute("value", percentage);
    });
}
<form action="#" method="POST">

  <div class="urls-container">
    <div class="url-pair">
      <input type="url" placeholder="1">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <div class="url-pair">
      <input type="url" placeholder="2">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <span></span>
  </div>

  <div>
    <div>
      <button type="button" id="addField">Add Field</button>
    </div>

    <div>
      <button type="submit">Create</button>
    </div>
  </div>

</form>

Sign up to request clarification or add additional context in comments.

4 Comments

Wow, my code actually worked except that. But what about the percentage values after deleting input fields?
This is amazing. Can't believe it works. Thanks a lot mate. Btw as you're the moderator, can you tell me which answer should I choose? The other guy has answered perfectly as well. I always get confused in these situattions
@Zak I'm not a moderator. What I can tell you is that you should always "up vote" all helpful answers. As for choosing "the" answer, you generally want the first one that you feel adequately answered/explained/solved your problem.
Thanks a lot mate.
1

So for the addition:

You've done right, except you want to add the new element BEFORE calculating the percentage. So, just put span.insertAdjacentHTML("beforeBegin", html) before calculating.

For the removal:

Again, you've done the removal right, you just need to calculate again the percentage after removing the element (like you did when adding the element).

To make things clearer, I put the calculating code inside a new function called calculatePercentage, and after each removal or addition of an element - this functio nis called in order to calculate new values.

Snippet:

const form = document.querySelector("form");
const span = document.querySelector("span");
const addBtn = document.querySelector("#addField");
const html = `
    <div class="url-pair">
        <input type="url" placeholder="3">
        <input type="text" class="perc">
        <button class="delete-btn" type="button">Delete</button>
    </div>
`;

let percInput = document.querySelectorAll('.urls-container .perc');
let percentage = (100 / percInput.length).toFixed(2).toString() + "%";

percInput.forEach((item) => {
    item.setAttribute("value", percentage);
});

const removeField = (e) => {
    if (!e.target.classList.contains('delete-btn')) return;
    e.target.parentElement.remove();
    calculatePercentage(); // Calculate after removal
}

// This function calculates the percentage
const calculatePercentage = () => {
    percInput = document.querySelectorAll('.urls-container .perc');
        percentage = (100 / percInput.length).toFixed(2).toString() + "%";

        percInput.forEach((item) => {
            item.setAttribute("value", percentage);
        });
}

// delete field
form.addEventListener('click', removeField);

// add field and divide input value automatically
addBtn.addEventListener('click', () => {

    span.insertAdjacentHTML("beforeBegin", html);
    
    calculatePercentage(); // Calculate after addition


});
<form action="#" method="POST">

  <div class="urls-container">
    <div class="url-pair">
      <input type="url" placeholder="1">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <div class="url-pair">
      <input type="url" placeholder="2">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <span></span>
  </div>

  <div>
    <div>
      <button type="button" id="addField">Add Field</button>
    </div>

    <div>
      <button type="submit">Create</button>
    </div>
  </div>

</form>

9 Comments

Thanks for detailed explanation.
hey man, I'm trying to include some more features in here and facing an issue. The problem is similar to this one which is adding new field. So would you help me figure it out?
Sure :) what exactly are you trying to achieve and what have you tried so far?
hey. please check this. I created a new post stackoverflow.com/questions/62859534/…
Hey mate. Yeah someone did. Thanks so much. Might need your help later though! :P
|
0

Something like this..

const form = document.querySelector("form");
const span = document.querySelector("span");
const addBtn = document.querySelector("#addField");
const html = `
    <div class="url-pair">
        <input type="url" placeholder="3" class="index">
        <input type="text" class="perc">
        <button class="delete-btn" type="button">Delete</button>
    </div>
`;

let percInput = [...document.querySelectorAll('.urls-container .perc')];
let percentage = (100 / (percInput.length)).toFixed(2).toString() + "%";

percInput.forEach((item) => {
    item.setAttribute("value", percentage);
});

const removeField = (e) => {
    if (!e.target.classList.contains('delete-btn')) return;
    e.target.parentElement.remove();
    recalculateRowVaues();
}

// delete field
form.addEventListener('click', removeField);

// add field and divide input value automatically
addBtn.addEventListener('click', () => {
    span.insertAdjacentHTML("beforeBegin", html);
    recalculateRowVaues();
});
function recalculateRowVaues() {
    percInput = [...document.querySelectorAll('.urls-container .perc')]; // So ist besser
    indexInput = [...document.querySelectorAll('.urls-container .index')];
    percentage = (100 / (percInput.length)).toFixed(2).toString() + "%";

    percInput.forEach((item) => {
        item.setAttribute("value", percentage);
    });
    indexInput.forEach((item, index) => {
        item.setAttribute("placeholder", index + 1);
    });
}
<form action="#" method="POST">

  <div class="urls-container">
    <div class="url-pair">
      <input type="url" placeholder="1" class="index">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <div class="url-pair">
      <input type="url" placeholder="2" class="index">
      <input type="text" class="perc">
      <button class="delete-btn" type="button">Delete</button>
    </div>

    <span></span>
  </div>

  <div>
    <div>
      <button type="button" id="addField">Add Field</button>
    </div>

    <div>
      <button type="submit">Create</button>
    </div>
  </div>

</form>

1 Comment

While you may have provided working code, "Something like this" doesn't explain the problem or your solution. Please read How to answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.