Gonzalo Portilla
Gonzalo Portilla

Reputation: 19

Transform elements to <input> with "Edit" button and save data with "Save" button

I have the following HTML:

<ul id="data"> 
    <li>Value 1</li>
    <li>Value 2</li>
    <li>Value 3</li>
    <li>Value 4</li>
</ul>

<br>

<div id="buttons">
<a id="edit">Click here to edit</a>
<br>
<a id="save">Click here to save</a>
</div>

I need to add an jQuery/Javascript snippet that transforms the <li> elements into <input> elements when the user clicks on the edit button, so he can write and change the values.

Once the process is done, with the save button the data backs to li with the values changed.

I have this jQuery code:

$('#edit').on('click',function() {
    $('#data').each(
        function(){
            if ($(this).find('input').length){
                $(this).text($(this).find('input').val());
            }
            else {
                var t = $(this).text();
                $(this).text('').append($('<input />',{'value' : t}).val(t));
            }
        });
});

But my idea is to separate this in two different buttons and I can't get it to work.

How can I do that? I know it shouldn't be difficult but the truth is I don't handle jQuery/Javascript very well.

Thank you in advance.

Upvotes: 0

Views: 1425

Answers (2)

Scott Marcus
Scott Marcus

Reputation: 65806

<li> elements must be contained within an <ol> or <ul> element, so your original HTML is invalid. Also, you don't need to transform the li into input elements, you just need to add contenteditible attributes to the li elements to make them editable.

Lastly, don't use a elements just as a click event hook. Any item can have a click event set up for it. <a> elements are for navigation.

// Get reference to the list
const list = document.getElementById("data");

document.getElementById("edit").addEventListener("click", function(evt){
  // Loop over the child elements
  list.querySelectorAll("li").forEach(function(li){
    // Make the element editable
    li.setAttribute("contenteditable","true");  
  });
});

document.getElementById("save").addEventListener("click", function(evt){
  // Loop over the child elements
  list.querySelectorAll("li").forEach(function(li){
    // Make the element editable
    li.removeAttribute("contenteditable");  
  });
});
#edit, #save { color:blue; cursor:pointer; }
<ul id="data"> 
    <li>Value 1</li>
    <li>Value 2</li>
    <li>Value 3</li>
    <li>Value 4</li>
</ul>

<br>

<div id="buttons">
<span id="edit">Click here to edit</span>
<br>
<span id="save">Click here to save</span>
</div>

Upvotes: 1

Xhynk
Xhynk

Reputation: 13840

You shouldn't need to convert the li elements to inputs, that needlessly complicates the process. You can just add the contenteditable attribute. You can still add input-handling events to these, they largely function like any other text based input element with that attribute enabled.

You can also change the button itself (both the inner text, and the function that fires when clicked in either state) instead of requiring two buttons.

var data = document.getElementById('data'),
    btn  = document.getElementById('edit'),
    lis  = data.querySelectorAll('li');
    
btn.addEventListener('click', function(e){
  lis.forEach(function(li){
    li.toggleAttribute('contenteditable');
  });
  
  if( lis[0].hasAttribute('contenteditable') ){
    // Currently editing, change the button
    btn.innerText = 'Click here to Save';
  } else {
    // We just "saved". run "save functions" here
    btn.innerText = 'Click here to Edit';
  }
});
[contenteditable] { outline: 1px solid #ccc; }
<ul id="data"> 
    <li>Value 1</li>
    <li>Value 2</li>
    <li>Value 3</li>
    <li>Value 4</li>
</ul>

<br>

<div id="buttons">
  <button id="edit">Click here to Edit</button>
</div>

If you do need two buttons, you can add a second button event handler, change toggleAttribute to addAttribute and removeAttribute, and remove the "inner text" changing code. But something like the above should be more than enough to get you started.

Also of note: li elements need to be inside a list container ol or ul, and a elements need an href attribute, so I've updated both of those to more semantically appropriate ul and button elements, respectively.

Upvotes: 2

Related Questions