joyi61
joyi61

Reputation: 41

select element that is created by insertAdjacentHTML for adding or remove class

guys i want to add one class to li Element aftet it is created through insertAdjacentHTML but i dont know how i can select it in fact i replaced insertAdjacentHTML way with append that i commented appen way; my codes are:

function todosGenerator(todosList) {
todoListElem.innerHTML = ''

    // let newTodoLiElem, newTodoLabalElem, newTodoCompleteBtn, newTodoDeleteBtn


    todosList.forEach(function (todo) {
    //     console.log(todo);
    //     newTodoLiElem = $.createElement('li')
    //     newTodoLiElem.className = 'completed well'

    //     newTodoLabalElem = $.createElement('label')
    //     newTodoLabalElem.innerHTML = todo.title

    //     newTodoCompleteBtn = $.createElement('button')
    //     newTodoCompleteBtn.className = 'btn btn-success'
    //     newTodoCompleteBtn.innerHTML = 'Complete'
    //     newTodoCompleteBtn.setAttribute('onclick', 'editTodo(' + todo.id + ')')

    //     newTodoDeleteBtn = $.createElement('button')
    //     newTodoDeleteBtn.className = 'btn btn-danger'
    //     newTodoDeleteBtn.innerHTML = 'Delete'
    //     newTodoDeleteBtn.setAttribute('onclick', 'removeTodo(' + todo.id + ')')

        // if (todo.complete) {
        //     newTodoLiElem.className = 'uncompleted well'
        //     newTodoCompleteBtn.innerHTML = 'UnComplete'
        // } **/! ican not write these lines with insertAdjacentHTML**

        // newTodoLiElem.append(newTodoLabalElem, newTodoCompleteBtn, newTodoDeleteBtn)

        // todoListElem.append(newTodoLiElem)



        todoListElem.insertAdjacentHTML('beforeend','<li class="completed well is"><label>'+todo.title+'</label><button class="btn btn-success" onclick="editTodo('+todo.id+')">Complete</button><button class="btn btn-danger" onclick="removeTodo('+todo.id+')">Delete</button></li>')

    

    })
    
}

Upvotes: 2

Views: 702

Answers (2)

Driftr95
Driftr95

Reputation: 4710

I think your commented code is better, but if you want to stick with the insertAdjacentHTML, you can alter to something like this:

    var liClass, cBtnInner;
    if (todo.complete) {
        liClass = 'uncompleted well';
        cBtnInner = 'UnComplete';
    } else {
        liClass = 'completed well is';
        cBtnInner = 'Complete';
    }
    todoListElem.insertAdjacentHTML(
        'beforeend', 
        '<li class="' + liClass + '"><label>' + todo.title + 
            '</label><button class="btn btn-success" onclick="editTodo(' + 
            todo.id + ')">' + cBtnInner + 
            '</button><button class="btn btn-danger" onclick="removeTodo(' + 
            todo.id + ')">Delete</button></li>'
    );

Upvotes: 1

zer00ne
zer00ne

Reputation: 43880

First off, do not use inline event handlers (see Why are inline event handler attributes a bad idea in modern semantic HTML?). Secondly, if you are dynamically adding buttons that trigger an event handler (a function that runs when an event is triggered like a "click" event), use event delegation by registering events to a common ancestor element.

In the example below, only the elements are generated and added to the DOM -- there is no event binding. A <form> is wrapped around the <ol> so that the succinct syntax of the HTMLFormElement interface can be used should it be needed (in this example it's used once ie const main = document.forms[0], but it can do a lot more with less code). See event handling for details as well.

Details are commented in example

const todo = [{
  title: "First task",
  status: "pending"
}, {
  title: "Second task",
  status: "pending"
}, {
  title: "Third task",
  status: "complete"
}, {
  title: "Fourth task",
  status: "pending"
}, {
  title: "Fifth task",
  status: "complete"
}];

/**
 * Generates a list from a given array of objects
 *  1. Adds a <ol> within a <form>
 *  2. Adds an <li> to <ol> for each object in >list<
 *  3. Within each <li> is an <output> and 2 <button>s
 *  4. Each object's "title" is the text of <output>
 *  5. Each object's "status" is the className of <li>
 * @param {array<object>} list - Each object is the following by default:
 *        {title: "TBA", status: "pending"}
 * @param {string||object} node - If it's a string then it's a selector of an element. Otherwise it's a
 *        DOM object. If undefined it is a @defualt of a selector called "body"
 */
function genList(list, node = "body") {
  let root = typeof node === "string" ? document.querySelector(node) : node;
  const form = document.createElement('form');
  const ol = document.createElement('ol');
  root.append(form);
  form.append(ol);

  list.forEach((item, index) => {
    ol.insertAdjacentHTML('beforeend', `<li class="${item.status}"><output name="title">${item.title}</output> <button name="complete" class="btn btn-success" type="button"></button><button name="delete" class="btn btn-danger" type="button">Delete</button></li>`);
  });
}

// Call genList pass the >todo< array and do not define second @param so it'll just add to <body>
genList(todo);
// Reference the <form>
const main = document.forms[0];
// Register the click event to <form> -- call action(event) when click event is triggered on <form>
main.onclick = action;

// Event handler passes Event object by default
function action(event) {
  // Reference the element the user clicked
  const clk = event.target;
  /*
  If the user clicked an element with [name="complete"]...
  ...toggle the classNames "complete" and "pending" on it's parent element
  In other words: if <button name="complete"...> was clicked...
  ...reverse the className of the containing <li> from/to "complete"/"pending"
  */
  if (clk.name === "complete") {
    clk.parentElement.classList.toggle("complete");
    clk.parentElement.classList.toggle("pending");
    return;
  }
  /*
  If the user clicked an element with [name="delete"]...
  ...find it's parent element and remove it
  */
  if (clk.name === "delete") {
    return clk.parentElement.remove();
  }
}
output {
  display: inline-block;
  width: 25ch;
}

button {
  display: inline-block;
  width: 12ch;
}

.complete output {
  text-decoration: line-through red;
}

.complete .btn-success::before {
  content: "Completed";
}

.pending .btn-success::before {
  content: "Complete";
}

Upvotes: 2

Related Questions