Home > Software design >  How can I run a function with an unavailable DOM object?
How can I run a function with an unavailable DOM object?

Time:05-02

I want to run a function to remove a button's parent element. parent element after getting it with

let rem_display_btn1 = document.getElementById('rem_display_btn1');

like this

let rem_display_btn1 = document.getElementById('rem_display_btn1');
rem_display_btn1.addEventListener('click', () => {
    let parentEl = rem_display_btn1.parentElement.parentElement;
    parentEl.remove();
});

The problem now is that the initial moment the script loads in the HTML file, the button is not yet there. That is: rem_display_btn1 is produced after another function is run and that function it depends on those not run on window.onload

I will also display the function below in case it will be of help:

// Cart list Table Element
let cart_list = document.getElementById('cart_list');
let numy = 0;

let htmlTR = '';

let cartUpdater = () => {

    cart_list.innerHTML = "";
    htmlTR = ''
    numy = 0;

    for(let a = 0; a < cart.length; a  ) {
        
        cart_list.innerHTML = "";
        numy  ;
        htmlTR  = `<tr id="htmlTR_ID${numy}">`;
        htmlTR  = `<td style="width: 7%;">${numy}</td>`;
        htmlTR  = `<td style="width: 25%;">${cart[a].name}</td>`;
        htmlTR  = `<td style="width: 25%;">${cart[a].price}</td>`;
        htmlTR  = `<td style="width: 15%; padding-left: 23px;"><button>-</button>1<button> </button></td>`;
        htmlTR  = '</tr>';
        console.log(htmlTR)
        cart_list.innerHTML  = htmlTR;
    }

    for(let b = 0; b < cart.length; b  ) {

        var remBtn_TD = document.createElement('td');
        remBtn_TD.style.width = '25%';
        var remBtn = document.createElement('button');
        remBtn.classList.add('rem_btn_');
        // Where the button element gets the **id: rem_display_btn1**
        // so I'm choosing the first button that is produced
        remBtn.id = `rem_display_btn rem_display_btn${b   1}`;
        remBtn.innerHTML = 'remove';
        
        remBtn_TD.appendChild(remBtn);

        console.log(cart_list.children[0].children[b]);
        cart_list.children[0].children[b].appendChild(remBtn_TD);        
    }

}

I've tried adding an if statement after the cartUpdater() function to check for document.getElementById('rem_display_btn1') before getting the element into a variable but even then it still doesn't exist

if (document.getElementById('rem_display_btn1')) {
    let rem_display_btn1 = document.getElementById('rem_display_btn1');
    rem_display_btn1.addEventListener('click', () => {
        let parentEl = rem_display_btn1.parentElement.parentElement;
        parentEl.remove();
    });
} else {
    console.log('Not There')
}

I'm thinking if there's a way to get the current state of the HTML/WebPage in total, so I also tried putting the if statement in a setTimeout and setInterval function but neither of them worked as well. The thing is I'm sure the is a way for this to work, I just can't figure it out.

Thanks in anticipation.

Adding the setInterval function:

setInterval(() => {
    if (document.getElementById('rem_display_btn1')) {
        let rem_display_btn1 = document.getElementById('rem_display_btn1');
        rem_display_btn1.addEventListener('click', () => {
            let parentEl = rem_display_btn1.parentElement.parentElement;
            parentEl.remove();
        });
    } else {
        console.log('Not There')
    }
}, 4000);

For the setTimeout:

setTimeout(() => {
    if (document.getElementById('rem_display_btn1')) {
        let rem_display_btn1 = document.getElementById('rem_display_btn1');
        rem_display_btn1.addEventListener('click', () => {
            let parentEl = rem_display_btn1.parentElement.parentElement;
            parentEl.remove();
        });
    } else {
        console.log('Not There')
    }    
}, 9000);

I used a longer time for the set timeout so I can run the cartUpdater() function before I get the rem_display_btn1 button

CodePudding user response:

You can also add a click listener to any parent element that exists right away (or the entire document) and check if the clicked element matches your button's id. This way, it does not matter if the button is added at a later point.

document.addEventListener("click", (e) => {
  if (!e.target.matches("#rem_display_btn1")) return;
  e.target.parentElement.remove()
})
<div>
  <button id="rem_display_btn1">Remove</button>
</div>

CodePudding user response:

You do not need the variable referencing the button at all:

document.addEventListener('DOMContentLoaded', () => {
   // Dropped: let rem_display_btn1 = document.getElementById('rem_display_btn1'); 
   document.getElementById('rem_display_btn1').addEventListener('click', (e) => {
        let parentEl = e.currentTarget.parentElement.parentElement;
            // Do not use 'this' to reference the button;
            //   Cf. https://stackoverflow.com/a/46270771/
        parentEl.remove();
   });
});
<div>
  <button id="rem_display_btn1">Remove</button>
</div>

Alternative 1 (Add event handler through the code that dynamically creates an element)

In case the element you want to attach the event handler to ( ie . rem_display_btn1) is inserted dynamically at some unspecified time after the document has initially been loaded, use the html attributes for event handlers:

function hnd_removeButton (e) {
    let parentEl = e.currentTarget.parentElement.parentElement;
    parentEl.remove();
}

document.addEventListener('DOMContentLoaded', () => {
     setTimeout ( () => {
         //
         // At the time you create the dynamic element, add the proper event handler.
         let e_btn = document.createElement('button')
           , e_container = document.getElementById ( 'test-anchor' )
           ;

         e_btn.setAttribute('id', 'rem_display_btn1');
         e_btn.textContent = 'Remove';
         e_btn.addEventListener('click', hnd_removeButton);

         e_container.appendChild(e_btn);
     }, 3000 ); 
});
<div id="test-anchor">
  <div>Waiting for button to appear</div>
</div>

Alternative 2 (Listen to DOM changes)

Another option is to respond to elements added to the DOM.

function hnd_removeButton (e) {
    let parentEl = e.currentTarget.parentElement.parentElement;
    parentEl.remove();
}

document.addEventListener('DOMContentLoaded', () => {
    document.addEventListener ( 'DOMSubtreeModified', (e) => {
        e.target.querySelector('#rem_display_btn1').addEventListener ( 'click', hnd_removeButton );
    });
     
setTimeout ( () => { // Demo only - Elements being added. The click event handler is attached in the event handler for 'DOMSubtreeModified'
         //
         // At the time you create the dynamic element, add the proper event handler.
         let e_btn = document.createElement('button')
           , e_container = document.getElementById ( 'test-anchor' )
           ;

         e_btn.setAttribute('id', 'rem_display_btn1');
         e_btn.textContent = 'Remove';

         e_container.appendChild(e_btn);
     }, 3000 ); 
});
<div id="test-anchor">
  <div>Waiting for button to appear</div>
</div>

  • Related