Martin Paul Eve bio photo

Martin Paul Eve

Professor of Literature, Technology and Publishing at Birkbeck, University of London

Email Books Twitter Google+ Github Stackoverflow MLA CORE Institutional Repo ORCID ID   ORCID iD

As part of the translation platform we’re building, I needed to implement the following workflow:

  • If the DOM has been modified previously, then restore the DOM and run the substitution function.
  • If the DOM hasn’t been modified, then just run the substitution function.

The problem was that whenever I ran $(“body”).html(this.original_document); in the first of these cases, the javascript would stop executing. I think this is because the object that triggered the event was destroyed, even though the script was in the head tag.

In other words, whenever I replace the body tag of the HTML, my javascript immediately stops and I can’t call the next function. I also tried using setTimeout to somehow defer this into a space that wouldn’t be picked up by garbage collection or anything else.

I eventually worked out that I can solve this problem by using DOM Mutation Observers. The first thing I do, now, when the page loads is setup a DOM Mutation Observer thus (I’m using a mixture of coffeescript and javascript here):

this.continue_action = ""
var target = document.body;
var observer = new MutationObserver(this.handle_state_continuity);
var config = { attributes: true, childList: true, characterData: true };

observer.observe(target, config);

This basically says call the handle_state_continuity function whenever the contents of the body changes at any depth, in any way. Inside the event-firing method, I then have:

  startSubstitution: (event = {}) =>
    if this.original_document == ""
      this.original_document = `$("body").html()`

    if this.original_document != `$("body").html()`
      this.continue_action = "substitute"
      console.log("Annotran: Restoring DOM to original state.")
      return null
      console.log("Annotran: Starting substitution directly.")

The important part to note here is that, in the case where it changes the body html, it first preserves the next action: this.continue_action = “substitute”.

The final part needed to glue this all together is the handle_state_continuity function, which for me looks like this:

  handle_state_continuity: (mutation = null) =>
    if this.continue_action != ""
      this.continue_action = ""
      console.log("Annotran: Starting substitution via state machine.")

And, tada! Now, when startSubstitution is called twice, the console output is:

Annotran: Starting substitution directly. [first time] Annotran: Restoring DOM to original state. [second time] Annotran: Starting substitution via state machine.