1

My goal is to update a movie. So here I have created a movie listing from the data pulled from the database. Now when I click the update button, I want the P elements to be replaced with input elements, that are holding the current P elements value. I want to delete the P element when the input element is created. Then when the user presses the save button, the input element is removed and the values from the input elements are stored in new P elements. Then i'll send that data to the database to be saved on the server side.

This code is what retrieves the movies from the database and generates the HTML.

// retrieve
getMoviesBtn.addEventListener('click', async(e) => {
  e.preventDefault();

  await axios.get('http://localhost:5000/movies/retrieve-movies')
    .then( res => {
      const movieList = res.data;

      for (let i = 0; i<movieList.length; i++) {
        const sections = document.createElement('section');
        sections.innerHTML = `
          <p class='id'> ${movieList[i].id} </p>
          <p id='title'> Title: ${movieList[i].title} </p>
          <p> Runtime: ${movieList[i].runtime} </p>
          <p> Release date: ${movieList[i].releaseDate} </p>
          <button class="delete-btn">Delete</button>
          <button class="update-btn">Update</button>
        `
        divMovieList.appendChild(sections);
      }
    });
  });

This is what I have so far

else if(e.target && e.target.textContent === 'Update') {
    const section = e.target.parentNode;
    const title = section.firstElementChild;
    const titleValue = title.innerHTML;
    const input = document.createElement('input');
    input.type = 'text';
    input.value = titleValue;
    section.insertBefore(input, title);
    section.removeChild(title)
  }

Now this works to remove the first P element and replace it with an input element. But how can I get to the other P elements in this same section, to do the same thing?

Thank you!

2
  • Usually in these situations I put all the controls in my original HTML document and use CSS display: none to hide the one that isn't needed at first, then use JS to change the CSS after the button click. Maybe I'm weird. Commented Jun 19, 2020 at 22:54
  • @mlibby, either that or have inputs by default which are disabled and can be enabled when the you want to edit the values. That way you don't have to switch elements. Commented Jun 19, 2020 at 23:06

1 Answer 1

2

There are multiple options for you to try.

You can select all <p> tags from the <section> element with querySelectorAll() and loop through each paragraph and create an input for it. Though you would still need a <form> element to wrap around the inputs to capture the data on submit.

And keep in mind, inputs need the name attribute to know which field belongs with each value. You could use the class attribute value as the name attribute value of each input you loop over.

else if(e.target && e.target.textContent === 'Update') {
  const section = e.target.parentNode;
  const paragraphs = section.querySelectorAll('p');
  for (const paragraph of paragraphs) {
    const name = paragraph.className;
    const input = document.createElement('input');
    input.name = name;
    input.type = 'text';
    input.value = paragraph.textContent;
    section.replaceChild(input, paragraph);
  }
}

Alternatively you could create multiple functions for each template that you use. Like a display template and an edit template where you switch between the two templates whenever based on if you need to display or edit your values.

const createDisplayTemplate = movie => `
  <p class="id"> ${movie.id}</p>
  <p class="title"> Title: ${movie.title}</p>
  <p class="runtime">Runtime: ${movie.runtime}</p>
  <p class="release-date">Release date: ${movie.releaseDate}</p>
  <button class="delete-btn">Delete</button>
  <button class="update-btn">Update</button>
`;

const createEditTemplate = movie => `
  <form>
    <input type="text" name="id" value="${movie.id}">
    <input type="text" name="title" value="${movie.title}">
    <input type="text" name="run-time" value="${movie.runtime}">
    <input type="text" name="release-date" value="${movie.releaseDate}">
    <button class="save-btn">Save</button>
  </form>
`;

const displayTemplate = createDisplayTemplate(movie);
const editTemplate = createEditTemplate(movie);

The next one is maybe the easiest method. You could insert both the content and the inputs, and toggle between them by adding or removing classes, whenever you need one of them. Like the example below.

const section = document.querySelector('section');
const display = section.querySelector('.js-display');
const edit = section.querySelector('.js-edit');

section.addEventListener('click', event => {
  const { target } = event;
  if (target.classList.contains('update-btn')) {
    display.classList.add('hide');
    edit.classList.remove('hide');
  } else if (target.classList.contains('save-btn')) {
    display.classList.remove('hide');
    edit.classList.add('hide');
  }
});

edit.addEventListener('submit', event => {
  event.preventDefault();
});
.hide {
  display: none;
}
<section>
  <div class="js-display">
    <p class="id">1</p>
    <p class="title"> Title: The Matrix</p>
    <p class="runtime">Runtime: 2h 16m</p>
    <p class="release-date">Release date: 1999</p>
    <button class="delete-btn">Delete</button>
    <button class="update-btn">Update</button>
  </div>
  <form class="js-edit hide">
    <input type="text" name="id" value="1">
    <input type="text" name="title" value="The Matrix">
    <input type="text" name="run-time" value="2h 16m">
    <input type="text" name="release-date" value="1999">
    <button class="save-btn">Save</button>
  </form>
</section>

Or as a last resort: change all of your <p> tags to <input> tags from the start. Disable the input tags so they can't be edited right away. On clicking the update button loop over the inputs and change the disabled property to either true or false.

The example below shows how this should look.

The only concern here is accessibility. Inputs are not text elements and will be read differently by screen readers and other tools. So if that is a concern to you then use one of the options above.

const section = document.querySelector('section');
const form = document.querySelector('.js-edit');

section.addEventListener('click', event => {
  const { target } = event;
  if (target.classList.contains('update-btn')) {
    for (const input of form.elements) {
      if (input.type === 'text') {
        input.disabled = !input.disabled;
      }
    }
  }
});

form.addEventListener('submit', event => {
  event.preventDefault();
});
form {
  display: flex;
  flex-direction: column;
}

input[type="text"] {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  margin: 10px 0;
  padding: 5px;
  background: #f7f7f7;
}

input[type="text"]:disabled {
  border: 0;
  background: none;
}
<section>
  <form class="js-edit" >
    <input type="text" name="id" value="1" disabled>
    <input type="text" name="title" value="The Matrix" disabled>
    <input type="text" name="run-time" value="2h 16m" disabled>
    <input type="text" name="release-date" value="1999" disabled>
    <button class="update-btn">Update</button>
    <button class="delete-btn">Delete</button>
  </form>
</section>

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

1 Comment

Wow how informative and helpful! You're amazing! I think i'll go with your first solution or your second solution. I like those two and they're more inline with my current code format. Thank you so much, this was awesome to come back to and see!

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.