Can we use a JavaScript MutationObserver to continuously monitor the presence and removal of elements?

Being new to the world of MutationObserver, I am trying to grasp how to detect the existence or permanent removal of an element. I stumbled upon an example that seemed promising, but it only works for detecting the element's presence once - not its removal or subsequent re-appearance.

HTML

<button onclick="appendS()">add message</button>
<button onclick="remove()">remove message</button>
<div id='imobserved'></div>

CSS

button{padding:30px; margin: 10px;}
#imobserved{border: 1px solid black; padding: 10px;}

JS

function appendS(){
  let s = document.createElement('span');
  s.id = "message";
  s.innerText = "some message !"
  document.querySelector('#imobserved').appendChild(s);
}

function remove(){
  document.querySelector('#imobserved').innerHTML = "";
}

function waitForAddedNode(params) {
    new MutationObserver(function(mutations) {
        var el = document.getElementById(params.id);
        if (el) {
            this.disconnect();
            params.done(el);
        }
    }).observe(params.parent || document, {
        subtree: !!params.recursive || !params.parent,
        childList: true,
    });
}

// Usage:

waitForAddedNode({
    id: 'message',
    parent: document.querySelector('#imobserved'),
    recursive: false,
    done: function(el) {
        alert('i see you')
    }
});

Answer №1

const observedEl = document.querySelector('#imobserved')

function append() {
  const messageSpan = document.createElement('span');
  messageSpan.id = "message";
  messageSpan.innerText = "This is a new message!";

  observedEl.appendChild(messageSpan);
}

function remove() {
  if (observedEl.lastChild) {
    observedEl.lastChild.remove();
  }
}

const observer = new MutationObserver(function(changes) {
  const changeType = changes[0].addedNodes.length > 0 ? 'Element added' : 'Element removed';
  alert(changeType);
});

observer.observe(observedEl, {
  subtree: true,
  childList: true
});
button {
  padding: 30px;
  margin: 10px;
}

#imobserved {
  border: 1px solid black;
  padding: 10px;
}
<button onclick="append()">Add New Message</button>
<button onclick="remove()">Remove Message</button>
<div id='imobserved'></div>

Answer №2

Here are some tips to help you achieve your goal. I made a few modifications to your code
First and foremost, avoid creating multiple DOM elements with the same ID as it is not considered good practice. The MutationObserver callback provides ample information, including details about added and removed nodes.


Feel free to test the snippet below to observe its behavior.

function appendS() {
  let s = document.createElement("span");
  s.innerText = "some message !";
  document.querySelector("#imobserved").appendChild(s);
}

function remove() {
  document.querySelector("#imobserved").innerHTML = "";
}

function waitForAddedNode(params) {
  new MutationObserver(function (mutationsList) {
    for (const mutation of mutationsList) {
      if (mutation.addedNodes.length) {
        params.onAdd(mutation.addedNodes);
      }

      if (mutation.removedNodes.length) {
        params.onDelete(mutation.removedNodes);
      }
    }
  }).observe(params.parent, {
    childList: true,
    subtree: true
  });
}

waitForAddedNode({
  parent: document.querySelector("#imobserved"),
  recursive: false,
  onAdd: function (elements) {
    alert("i see you added new element");
    console.log(elements);
  },
  onDelete: function (elements) {
    alert("i see you removed " + elements.length + " elements");
    console.log(elements);
  }
});

const addBtn = document.querySelector("#add");
const removeBtmn = document.querySelector("#remove");

addBtn.addEventListener("click", appendS);
removeBtmn.addEventListener("click", remove);
button {
  padding: 30px;
  margin: 10px;
}
#imobserved {
  border: 1px solid black;
  padding: 10px;
}
<body>
    <button id="add">add message</button>
    <button id="remove">remove message</button>
    <div id="imobserved"></div>
</body>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Combining several position-aligned classes into a single div instead of each having their own individual div

http://jsfiddle.net/58arV/ Trying to create a question component where users can choose the correct answer out of 4 options. I planned to use 4 input styles with a checkmark "radio" DIV on the right. When a user clicks on an option, it should display a c ...

Tips for shrinking grommet's webpack bundle size

Hey there! Recently, I started exploring Grommet and worked on my first application by following their step-by-step guide. However, I noticed that the webpack bundle created for production is quite large, exceeding 1.7 MB even though I haven't added a ...

Struggling to make vue-i18n function properly with nuxt generate?

In an effort to enhance my skills, I am creating a small Nuxt project from scratch. While the project runs smoothly on the local server, I have encountered an issue with the language switcher not updating the translation fields when using nuxt generate. U ...

Is it possible to remove a form element without relying on jQuery's .remove() method?

Looking to hide the select all checkbox from users without actually removing it when submitted. The challenge lies in making the removal of the select all checkbox seamless on the front end, while ensuring it is not sent to the server. Is there a way to ...

I'm having issues with my navigation bar, and the border on the right side is not functioning correctly

I've been struggling to get the right border positioned between each section of my nav bar. I've tried wrapping each word in a separate span, but it doesn't seem to cooperate. The borders end up on the side and are too small to fill the enti ...

What is the best way to incorporate a <c:forEach> loop into JSP operations?

I am attempting to calculate the product of two variables that are associated with an item in a loop. Below is the code snippet from the JSP file: <table> <thead> <tr> <th>PRICE</th> <th ...

What could be the reason behind the expansion of other <td> elements because of my <td> content?

Can you check out this demo: http://jsfiddle.net/wd9G8/1/ I've been encountering an issue with the "Problem Widget" where adding content causes the bottom row of tds to expand. To elaborate, I'm trying to insert a div with a slideshow plugin as ...

getting an unexpected outcome while transferring data from a service to my angular element

I recently developed an Angular service with a basic string variable that gets updated within the service. The issue arises when trying to access the updated value in my component.ts file after calling the function responsible for updating it. Below is th ...

Footer rises with bootstrap zooming

Hey everyone! I'm a beginner in the world of web development and currently working on creating my own website. I've run into an issue with my footer - I want it to stay fixed at the bottom even when zoomed in, but for some reason, when I zoom in, ...

What is the best way to retrieve input attributes in Node.js?

How can I achieve the same functionality as the code snippet below using Node.js? selectedRadio = document.querySelector('input[name="device"]:checked').dataset.name; I am developing a backend using Node.js and Express.js with BodyParser for an ...

The CSS styles are not recognized by the Windows 2003 server

After deploying my webapp, which was created using asp.net, to a testing server (Windows 2003 SP2, IIS6), I encountered an issue. When I accessed the default page, none of the CSS styles were applied. Only plain text or formatting from the .aspx file was v ...

Using ng serve to upload multipart files in Angular 2

I am currently working on the front end of a file uploading service and have encountered a strange issue. When I restart the server using ng serve, it throws an error related to generated components within the app component. The error message can be seen h ...

Struggling to identify the significance using the for-in loop

I'm trying to iterate through the elements of this array and return 'gotcha, bill' when the 'for in' loop finds the value 'bill'. However, all I get is 'not him' repeated four times. I've gone through my co ...

Filtering data from Arduino using web serial communication

My setup consists of an Arduino kit connected to a webserial API that sends data to an HTML div identified by the id 'target'. Currently, all the data is being logged into one stream despite having multiple dials and switches on the Arduino. Th ...

Printing a specific column of a particular row in an HTML table to the printer: Tips and tricks

How can I print a specific column from a particular row in an HTML table? I've attempted various CSS and jQuery methods I found online, but none of them seem to be effective. ...

Exploring Vue.js: Pre-viewing an Image Before Updating it

Hey there! I have a custom component where I am trying to display an image. I want to show a preview of the image after selecting it, but before uploading it to the server. Can someone please help me with this? I attempted to use the v-on:change event on ...

Unable to retrieve button value with material-ui package

My task requires me to retrieve the value of a button, as it contains an ID essential for further steps. Initially, I used a standard button with Bootstrap styling and everything functioned correctly. <button value={row.vacationRequestID} c ...

Prevent overlapping of divs

Having some trouble with my portfolio page. I've got a section set up with nested divs, but when I try to adjust the position of the divs, they end up on top of each other. Can anyone offer some guidance? #portfolio { width: 650px; background-col ...

What is the best way to manage a hover effect on devices such as iPads?

As a beginner in web development, I am currently facing a challenge when it comes to managing hover effects on devices like iPads. Initially, my solution was to remove the CSS for hover and replace it with click events, but my client prefers to keep the ho ...

What is the best way to retrieve the js window object within emscripten's EM_JS function?

I'm looking to access the window.location in an EM_JS method in order to call a JavaScript method from C++. My attempted approach was: EM_JS(const char*, getlocation, (), { let location = window.location; let length = lengthBytesUTF8(location ...