I mostly just want to make sure I've understood the specs correctly with this code, but here goes:

var nums = document.getElementsByTagName('x-number');
var observer = new MutationObserver(replaceNumerics);
var config = { childList: true, subtree: true };
observer.observe(document.body, config);
replaceNumerics(); // initial load

function replaceNumerics() {
    var l = nums.length, i, span;
    for( i=l-1; i>=0; i--) { // node list is live so iterate backwards
        span = document.createElement('span');
        span.textContent = parseFloat(nums[i].textContent).toLocaleString();

The idea is to have a custom <x-number> tag that contains... a number, obviously. JavaScript will detect these tags, parse out the number, then render it according to the user's locale. I'm planning to do a similar thing for dates, which will be especially useful since <input type="date" /> already uses the user's locale.

My biggest concern is I think this will result in an infinite loop, as the replaceChild call will fire the MutationObserver again... but if I understand the spec correctly, this second firing will only happen if and when all <x-number> elements have been replaced (as opposed to the earlier DOMSubtreeModified event which would fire immediately), at which point there would be no more <x-number> elements to replace and the loop would end.

Honestly this is all very unfamiliar territory to me, but I believe this should work fine... But I figured I'd subject it to public opinion before going any further with it.

  • \$\begingroup\$ Have you tested your code to see if it actually works or not? \$\endgroup\$ – Phrancis Sep 5 '16 at 23:00
  • \$\begingroup\$ Yes, and it seems to work. But a lot of stuff I write "seems to work" :D \$\endgroup\$ – Niet the Dark Absol Sep 5 '16 at 23:02
  • \$\begingroup\$ There won't be an infinite loop because you iterate over the custom tags only, so even though the observer callback will be invoked after modification it'll do nothing, and won't trigger subsequent invocation. \$\endgroup\$ – wOxxOm Sep 6 '16 at 8:59
  • \$\begingroup\$ @wOxxOm And that's because the custom tags are removed in the process, so there's none left when it runs the second time, right? \$\endgroup\$ – Niet the Dark Absol Sep 6 '16 at 9:04
  • \$\begingroup\$ Just remembered that getElementsByTagName is a live list, so removing the elements in the loop will not work as intended unless I iterate backwards... \$\endgroup\$ – Niet the Dark Absol Sep 6 '16 at 9:10

One issue I'm seeing is that you're listening to the entire tree. While not an issue in small apps, it might be an issue for huge apps, with deep trees. Consider limiting it to the places where you expect your custom tag will exist.

If you are in control of the process of adding these elements to the DOM, consider doing this transformation there instead of listening to the entire DOM for you to change it. It will be easier to debug since the transformation is involved in the process of generating that element, instead of an indirect side-effect of some other operation.

You know that feeling when someone else's JavaScript re-arranges the HTML you rendered on the backend and you had to hunt it down? Same story.

  • \$\begingroup\$ One could argue that for huge trees this approach isn't going to scale up either way and OP would be best off leveraging something like Vue or Polymer and creating custom elements. Scanning the tree, as you mention, will cause huge performance issues. \$\endgroup\$ – Dan Sep 7 '16 at 6:45
  • \$\begingroup\$ I do like to keep the DOM tree relatively simple, but I can certainly see the potential for issues here. The main reason for the Observer at all is to avoid hooking into jQuery's .html(), but come to think of it I could extend my AJAX setup to handle the replacements in the returned data. You've certainly given me some options to think about. Thank you. (Also, I'm the only dev on this project, and an appropriate tag choice and documentation should make this a non-issue if I ever hire another dev) \$\endgroup\$ – Niet the Dark Absol Sep 7 '16 at 10:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.