boliwe
boliwe

Reputation: 233

Javascript object destroy event handler

MyMapStaticObject

var PlaceViewModel = function(){
    MyMapStaticObject.addLayer(someLayer);
}

PlaceViewModel.prototype.addMarker = function(item){

}

I have a PlaceViewModel that has a function named addMarker to add marker to map. I will use PlaceViewModel new istances in different classes.

var inst = new PlaceViewModel();

When I initialize the PlaceViewModel, I am adding new layer to map via MyMapStaticObject. I should remove layer when instance destroyed.

Can I handle javascript destroy event?

Upvotes: 3

Views: 15019

Answers (2)

Keith
Keith

Reputation: 24221

Bit late to the party here, but I wanted to know when an object got destroyed. Problem is JS doesn't have any built in way of doing this. I wanted this so that I could add websocket events to a page, and then remove when it went to another page, of course I could have implemented this in say the page loading section, but I have different frameworks and wanted a more generic solution to object destroying problem. Javascript is a garbage collected language, but it still would have been nice to have some life-cycle events we could attach too. There is of course proxy's, but again that wouldn't help here, because it's the proxy itself I would need to know that has been deleted.

Well, there is one place you do get a kind of destroy event, and that's with the MutationObserver, that most modern browsers now support. It's not strictly destroying, it's adding and removing nodes from the DOM. But generally speaking if you have events, you are likely to have some DOM node you could attach too, or even if it's none visual you could just add a none visible DOM element.

So I have a little function called domDestroy(element, obj), when it detects the element been removed, it then checks if the obj has a destroy method, if one exists it will call it.

Now one gotcha I had is that I create my pages in an hidden DOM node, and of course when I placed into the visible DOM node, I was getting a delete because I was detaching from the invisible DOM node, and then attaching to the visible DOM. Not what we want at all.

The solution was pretty simple, when doing this kind of double buffering, it's normally done in 1 step, eg. hide current page, show new page. So what I do is keep track of when it's been removed and keep in a simple Set, and then also keep track of elements been added, and if this element is part of the Set I will remove it. I then just check this Set again on the next tick, if's it's still there, it's been really deleted and we call the destroy method of the object.

Below is a simple example, basically if you right click and inspect the page, you can move the LI's up and down with dragging and dropping, this would cause a DOM detach and re-attach,. But if you instead delete one of the LI's, you will notice it say delete then, because it now knows it wasn't re-attached to another DOM.

Of course, one thing to be aware of, if you do any attaching / detaching of DOM elements try and do this within the same tick, IOW: be aware of asynchronous ops in between. Also you might use detached DOM's to build your pages, here you could easily alter the function to cope with this too, basically add these using the destroyObserver.observe(.

const dsy = "__dom-destroy-obj";
const destroyList = new Set();
let tm;

function cleanUp() {
  tm = null;
  for (const el of destroyList) {
    for (const d of el[dsy]) {
      d.destroy();
    }
  }
  destroyList.clear();
}

function checkDestroy(el) {
  if (el[dsy]) {
    for (const d of el[dsy]) {
      if (!d.destroy) {
        console.warn("No destroy, on dom-destroy-obj target");
      } else {
        destroyList.add(el);
        if (tm) return; //already a timer running
        tm = setTimeout(cleanUp, 1);
      }
    }
  }
  if (el.childNodes) for (const n of el.childNodes) checkDestroy(n);
}

function checkAdded(el) {
  if (el[dsy]) {
    destroyList.delete(el);
  }
  if (el.childNodes) for (const n of el.childNodes) checkAdded(n);
}

const destroyObserver = new MutationObserver(
  function (mutations) {
    for (const m of mutations) {
      if (m.removedNodes.length) {
        for (const i of m.removedNodes) {
          checkDestroy(i);
        }
      }
      if (m.addedNodes.length) {
        for (const i of m.addedNodes) {
          checkAdded(i);
        }
      }
    }
  }
);

destroyObserver.observe(document.body, {
  childList: true,
  subtree: true
});

function domDestroy(element, obj) {
  if (!element[dsy]) element[dsy] = new Set();
  element[dsy].add(obj);
}


//simple test.

for (const i of document.querySelectorAll("li")) {
  domDestroy(i, {
    destroy: () => console.log("destroy")
  });
}
<span>
From your browsers inspector, try moving the LI's, and deleting them.  Only when you delete the DOM node, should the destroy method get called.
</span>


<ul>
  <li>Re order</li>
  <li>Or delete</li>
  <li>Some of these</li>
</ul>

Upvotes: 3

jfriend00
jfriend00

Reputation: 707706

Javascript does not have a destroy event. It is a garbage collected language and will free an object when there is no longer any code that can reach the object reference. When it does free the object, it does not provide any event to notify of that.

If you want to implement some sort of clean-up code that will remove the layer, then you will have to add a method that you can call when you are done with the object, so you can call that method and it can then remove the layer in that method. Calling this method will have to be a manual operation on your part (most likely it will be hooked into the management of other things going on in your code and you can call it by that code at the appropriate time).

Upvotes: 5

Related Questions