5

I have the following code:

<div>
  <span>Random text</span> 
  <span style="color: red">Random text</span>
</div>

and, using code, I have a 50% chance of changing the order of the elements inside the div.

$(document).ready(function() {
  var initial = $("div"); //This might not be ok

  if (new Date() % 2) //50% chance to randomize order 
    randomizeOrder();

  var after = $("div"); //Also this might not be ok
  //Detect if the order of spans changed
  if (after != initial) //Definitely won't work
    console.log("Order changed");
})

function randomizeOrder() {
  var lastElement = $("div span").last();
  $(lastElement).insertBefore($("div span").first());
}

This example is oversimplified. The ordering is done by the user using a 3rd party library which won't tell me if the order actually changed, just tell me when it started & finished.

How can I detect if the order of the DOM elements actually changed? The information I have inside them cannot be used, as it can be exactly the same, as in the example.

Fiddle

iuliu.net
  • 6,666
  • 6
  • 46
  • 69
  • Why can't you use a *-data attribute in the span elements that provides a unique index for the elements? – Scott Marcus Nov 02 '16 at 15:16
  • `$(() => { $("div span").each((idx, el) => $(el).data("index", idx)) })` – Andreas Nov 02 '16 at 15:16
  • I was wondering if checking for actual DOM elements references wasn't doable – iuliu.net Nov 02 '16 at 15:17
  • Something like [this](https://jsfiddle.net/p2xkcooo/) (warning: poor code quality^^) ? – Andreas Nov 02 '16 at 15:30
  • @iuliu.net, I think when you are asking about DOM changes, the answer that is more accurate is the one I gave (regarding the MutationObserver). If someone will find this question (on search In google) the solution they will be looking for is the one in my answer. Do you mind changing the accepted answer to mine? – Dekel Dec 05 '16 at 11:22
  • Sure. I did find your solution very elegant initially, but it eventually just didn't do it for my issue. But then again, that would be my fault as I had oversimplified the example. Nevertheless, for my question as put here your answer is indeed the solution, so yes, I'll revise it. :) – iuliu.net Dec 07 '16 at 08:24
  • Didn't notice your change! Thanks for that :) I'll vote up your answer aswell. Thanks again! – Dekel Dec 22 '16 at 00:42

2 Answers2

17

If you want to check for changes in the DOM you can use the MutationObserver:

$(document).ready(function() {
  var initial = $("div"); //This might not be ok

  if (new Date() % 2) {
    randomizeOrder();
  }
  
});

function randomizeOrder() {
  var lastElement = $("div span").last();
  $(lastElement).insertBefore($("div span").first());
}

var target = document.getElementById('id1');

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  var changed = false;
  mutations.forEach(function(mutation) {
    // You can check the actual changes here
  });
  console.log('Dom Changed');
});

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

// pass in the target node, as well as the observer options
observer.observe(target, config);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="id1">
  <span>Random text</span> 
  <span style="color: red">Random text</span>
</div>

The MutationObserver gives you the ability to check for DOM changes in a specific element, without any requirement regarding the code the changes the DOM.

Dekel
  • 60,707
  • 10
  • 101
  • 129
1

I've finally come up with the solution that does just what I wanted and in the way I thought of initially but wasn't sure how to accomplish it:

  var before = Array.prototype.slice.call($("div")[0].children).map(x => x);

  if (new Date() % 2) //50% chance to randomize order 
    randomizeOrder();

  var after = Array.prototype.slice.call($("div")[0].children).map(x => x);

  //Detect if the order of spans changed
  var hasChanged;
  after.forEach((el, i) => {
    if (el != before[i]) {
      hasChanged = true;
    }
  })
  hasChanged && console.log("Something's changed");

The secret is to basically hold references to the children before and after the ordering and then compare them, but NOT a direct reference to element.children, as it updates together with the DOM, but rather create new arrays, and have inside them the children, in current order.

Updated fiddle

iuliu.net
  • 6,666
  • 6
  • 46
  • 69