1

I am looking for a way to watch for dynamically populated (no page reload) content inside an element so that I can add a class to another element.

I have this so far:

HTML
<div class="message-container">
  <div class="messages error"><span></span></div>
  <div class="messages success"><span></span></div>
  <div class="messages warning"><span></span></div>
  <div class="messages warning"><span></span></div>
</div>

<div class="myDiv">...</div>
JS
$(document).ready(function() {
  if($.trim($("div.messages.success span").html()) != '') {
    $('.myDiv').addClass('.with-message');
  }
});

How can I have myDiv to have the with-message class when any message span is populated?

Note it ay be populated on the fly without reloading, or exist when landing on the page, or be populated with a reload.

Legends
  • 21,202
  • 16
  • 97
  • 123
Matthew Woodard
  • 754
  • 2
  • 10
  • 26

5 Answers5

1

Two approaches:

1.) Use MutationObserver (supported IE11<)

// select the target node
var target = $(".success")[0];
 
// create an observer instance
var observer = new MutationObserver(function(mutations) {
    // mutations.forEach(function(mutation) {
        console.log("div.success subtree changed");
    // });
});
 
// configuration of the observer:
var config = { childList: true}; // ,subtree: true, characterData: true 
 
// pass in the target node, as well as the observer options
observer.observe(target, config);
 

setTimeout(function(){
  $(".messages").each(function(item,x){
    x.innerHTML = "content";
  });
},1000);
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<div class="message-container">
  <div class="messages error"><span></span></div>
  <div class="messages success"><span></span></div>
  <div class="messages warning"><span></span></div>
  <div class="messages warning"><span></span></div>
</div>
</body>
</html>

2.) Use the !deprecated! DOMSubtreeModified event. (Not recommended)

You can try the following (jsFiddle):

 
$('.messages').bind("DOMSubtreeModified",function(){
  console.log('changed');
});

setTimeout(function(){
  $(".messages").html("content");
},1000);
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  
<div class="message-container">
  <div class="messages error"><span></span></div>
  <div class="messages success"><span></span></div>
  <div class="messages warning"><span></span></div>
  <div class="messages warning"><span></span></div>
</div>
</body>
</html>

One side effect I noticed, the event is fired twice.

Warning:

The DOMSubtreeModified event type is defined in this specification for reference and completeness, but this specification deprecates the use of this event type.

Support:

DOMSubtreeModified is not supported in IE8 (and below)

Legends
  • 21,202
  • 16
  • 97
  • 123
1

You can use a MutationObserver to detect when a child was added.

// select the target node
var target = document.querySelector(".messages.success");

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  document.querySelector(".myDiv").classList.toggle("active", mutations[0].addedNodes.length);
});

// configuration of the observer:
var config = {
  childList: true
};

// pass in the target node, as well as the observer options
observer.observe(target, config);

// later, you can stop observing
//observer.disconnect();

window.setTimeout(function() {
  var temp = document.createElement("div");
  temp.innerHTML = "Hello";
  document.querySelector(".messages.success").appendChild(temp);
}, 5000);
.active {
  background-color: green;
}
<div class="message-container">
  <div class="messages error"><span></span>
  </div>
  <div class="messages success"><span></span>
  </div>
  <div class="messages warning"><span></span>
  </div>
  <div class="messages warning"><span></span>
  </div>
</div>

<div class="myDiv">...</div>

Or you can just poll the element and see if it has any children

epascarello
  • 204,599
  • 20
  • 195
  • 236
0
$(document).ready(function() {
  updateDiv();
});

function updateDiv(){
  $('.myDiv').removeClass('.with-message');
  $.each($(".messages span"), function(){
    if($(this).text().length>0){
      $('.myDiv').addClass('.with-message');
    }
  });
  setTimeout("updateDiv()",3000);//periodically check for changes
}
Tech AG
  • 64
  • 3
-1

Assuming the user types into the field in question, all you need to do is run the check whenever a keyup or keydown or keypress event is triggered for the element in question:

$(document).ready(function() {
  $("div.messages.success span").keyup(function(){
    if($.trim($("div.messages.success span").html()) != '') {
      $('.myDiv').addClass('with-message');
    }
  });
});

Note that addClass() should not pass the . along with the class name.

Assuming AngularJS is populating your fields (as your comments in the question say), you should apply the class directly with Angular, similar to:

app.controller('Ctrl', function($scope) {
    $scope.click = function() {
      angular.element('.myDiv').addClass('with-message');
    };
});

Hope this helps!

Obsidian Age
  • 41,205
  • 10
  • 48
  • 71
-1

In addition to running initially, you can listen for new elements being created:

document.addEventListener("DOMNodeInserted", function(event) {
      if($.trim($("div.messages.success span").html()) != '') {
          $('.myDiv').addClass('.with-message');
      }  
})

Alternatively, take a look at Jquery's live call: http://api.jquery.com/live/

EDIT: The better approach would be to assign the class as you insert the new elements.

ioseph
  • 1,887
  • 20
  • 25