Destroying a Javascript API

Problem:

  • A third party JS API doesn’t have a destroy method.
  • The JS API can only be instantiated once per page view.
  • Writing a single page app by its nature requires a dynamic DOM
  • Dynamic DOM will require the JS API to be spun up and destroyed on multiple elements during the life cycle of the page view

What to do?

Answer: Destroy it yourself. By highjacking the events as they are created, you can gather a collection of all events that the JS API has created during instantiation. When it’s time to destroy the JS API, reference your collection of events to remove them from the window:


var _apiE 		= []; // Array of JS API Events.
var _scriptTag 	= null; 

var _methods: {

	initJsApi: function() {
		// Hook the native event listener to capture JS API events.
		// Preserve the original native method.
		var ogAddEventListener = window.addEventListener;

		// Override the native method.
		window.addEventListener = function() {

			// Capture JS API events.
			_apiE.push({
				type: 			arguments[0],
				listener: 		arguments[1],
				useCapture: 	arguments[2]
			});

			// Call the original native method after capturing the event 
			// so JS API and the browser do their business as usual.
			ogAddEventListener.apply(this, arguments);
		};

		// Add the <script> tag to the DOM dynamically to load the JS API.
		// jQuery will inject ?_0000 cache buster to script tag src attribute value.
		// Some sites will not allow the JS API to be requested with the querystring present.
		// Use native element to work around this.
		_scriptTag = document.createElement('script');
		_scriptTag.setAttribute('src', 		'https://js-api-cdn.com/js/third-party-api.min.js');
		_scriptTag.setAttribute('type', 	'text/javascript');
		_scriptTag.setAttribute('id', 		'js-api-script');
		$('html').append(_scriptTag);

		// Wait for the JS API to instantiate after the <script> has been attached to the DOM.
		var intvl = setInterval(function() {

			if (window.jsApi) { // The actual name of the API object would go here.

				clearInterval(intvl);

				// Instantiate the JS API.
				window.jsApi.init({ options });

				// Reset the native event listener to the original method so we stop capturing events.
				window.addEventListener = ogAddEventListener;
			}
		}, 5);
	},

	destroyJsApi: function() {

		// Remove the <script> tag and instance of the JS API.
		$('#js-api-script').remove();
		delete window.jsApi;

		// Remove the events that the JS API added.
		$.each(_apiE, function(i, e) {

			window.removeEventListener(e.type, e.listener, e.useCapture);
		});
	}
};

Danger: other events that are registered while waiting for the JS API to instantiate will also get removed.