user3149173
user3149173

Reputation: 41

Can mutation observer listen for changes in "data-" attributes?

So the problem is that I have an html element which has an object in a "data-" attribute (set via jQuery ofc) and I want to listen to changes in that attribute.

I have already tried a lot of things, like setting almost all possible combination values in the MutationObserverInit object, but none of this help.

Does anybody know if it is possible?

$('#some-id').click( function() {
  //$('#some-id').attr('title', 'some-title'); //this works
  $('#some-id').data('foo', 'bar1'); //this don't
});

var functionCallBack = function(mutations) {
  alert('something changed')
}

// select the target node
var target = document.getElementById("some-id");

// create an observer instance
var observer = new MutationObserver(functionCallBack);

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

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

$('#some-id').data('foo', 'bar');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="some-id">HEY</div>

Upvotes: 4

Views: 4617

Answers (2)

Kalimah
Kalimah

Reputation: 11437

New Answer (Based on Oriol's Answer):

I think Oriol provided a better approach but it could be optimized further.

var object = {
  'key': 'value'
};

$('.addObj').click(function() {
  $('#some-id').observeData('foo', object, function() {
    console.log("Object Added");
  });
});

$('.removeObj').click(function() {
  $('#some-id').observeData('foo', null, function() {
    console.log("Object removed");
  });
});


jQuery.fn.observeData = function(name, object, callback) {
  // Get elemenet
  var element = $(this[0]);

  // Add data
  element.data(name, object);

  // Call the callback function
  callback();

  // Return this for a chainable function
  return this;
};
span {
  padding: 10px;
  float: left;
  margin: 5px;
  cursor: pointer;
  background-color: green;
  border: 1px solid black;
  border-radius: 5px;
  transition: all 0.5s;
  color: white;
  font-size: 13px;
}
span:hover {
  opacity: 0.8;
}
#some-id {
  text-align: center;
  width: 100%;
  margin: 10px 0;
  font-size: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="some-id">HEY</div>

<span class='addObj'>Add Object</span>
<span class='removeObj'>Remove Object</span>

JSFiddle: https://jsfiddle.net/t012rb9j/1/

Old Answer:

As you stated in your code:

$('#some-id').click(function() {

 // $('#some-id').attr('title', 'some-title'); //this works
 $('#some-id').data('foo', 'bar1'); //this don't
});

.attr() works while .data() is not working. The reason is that .attr() adds an an attribute value to your HTML tag while data stores this data in memory.

From jQuery documentation:

.data(): Store arbitrary data associated with the specified element and/or return the value that was set.

.attr(): Get the value of an attribute for the first element in the set of matched elements or set one or more attributes for every matched element.

One way to detect data changes (if you want to store an object) is to combine both functions.

var object = {
  'key': 'value'
};

$('.addObj').click(function() {
  $('#some-id').data('foo', object).attr("data-attached", "true");
});

$('.removeObj').click(function() {
  $('#some-id').data('foo', null).attr("data-attached", "false");
});


var functionCallBack = function(mutations) {
  mutations.forEach(function(mutation) {
    if (jQuery(mutation.target).attr("data-attached") == "true") {
      // Your code here
      console.log("Object Added");
    } else {
      console.log("Object removed");
    }
  });
}

// select the target node
var target = document.getElementById("some-id");

// create an observer instance
var observer = new MutationObserver(functionCallBack);

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

// pass in the target node, as well as the observer options
observer.observe(target, config);
.addObj, .removeObj {
  padding: 10px;
  float: left;
  margin: 5px;
  cursor: pointer;
  background-color: green;
  border: 1px solid black;
  border-radius: 5px;
  transition: all 0.5s;
  color: white;
  font-size: 13px;
}

.addObj:hover, .removeObj:hover {
  opacity: 0.8;
}

#some-id {
  text-align: center;
  width: 100%;
  margin: 10px 0;
  font-size: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="some-id">HEY</div>

<span class='addObj'>Add Object</span>
<span class='removeObj'>Remove Object</span>

JSFiddle: https://jsfiddle.net/9xkb6jv4/

Upvotes: 3

Oriol
Oriol

Reputation: 288130

If you want to detect new data set with jQuery's data, you can always hijack it.

var functionCallBack;
var $data = jQuery.fn.data;
jQuery.fn.data = function(key, value) {
  var ret = $data.apply(this, arguments);
  if(functionCallBack && this[0] && value !== undefined) {
    functionCallBack(this, key, value);
  }
  return ret;
};

$('#some-id').click( function() {
  $(this).data('foo', 'bar1');
}).data('foo', 'bar');
functionCallBack = function(elements, key, value) {
  console.log('something changed')
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="some-id">HEY</div>

Upvotes: 1

Related Questions