Forum Academy Marketplace Showcase Pricing Features

Isolating plugin scripts

Hi, just wondering if anyone has come across a good way to isolate scripts used in a particular plugin?

I’m building a plugin that uses the Google Maps javascript api, specifically the Places library. If I load the Google Maps api in my plugin, then if the plugin is placed on a page that includes another plugin that also uses the Maps api, like a Map element, it breaks because the API errors if loaded twice. Console error:

You have included the Google Maps API multiple times on this page. This may cause unexpected errors.

Unfortunately I can’t just check if the Google Maps API has been loaded already and not load it again if it has, because the other elements don’t include the places library, which has to be specified in the script URL in order to load, like:

//maps.googleapis.com/maps/api/js?libraries=places

This seems to be an issue others have run into often elsewhere, but the stackoverflow solutions are always “just load the maps API once and specify all the libraries you need”. Not much help when you don’t control the source of other plugins on the page.

If anyone has any ideas of how to approach this issue I’d be very grateful!

2 Likes

Actually this is an important question as plugin ecosystem grows. How does bubble prevent loading of same script from multiple plugins?

6 Likes

I’ve run into the same problem with the Font Awesome svg script.

Did some more research and apparently this is a big issue for Wordpress plugin builders, too. Wordpress provides a function to dequeue scripts loaded by themes and other plugins: https://codex.wordpress.org/Function_Reference/wp_dequeue_script

Would be nice to have something like this available, though it’s probably more to be thought about some time down the line, since the plugin ecosystem is still relatively new.

Personally I thought the same in the start, For some reason building a plugin for bubble made me feel as if bubble held the responsibility for the potential issues around what may happen if two developers products clash. Its not the way it works when your building out a full lib or some speccy new SDK or self hosted self scripted server instances with your own set of plugin builders because you build with compatibility in mind at scale.

Im not saying I have the answers to the perfect recipe & im certainly not saying this a standard of sorts but the way I go about this issue is fairly simple. I assume that each external is there already and rather than doubt it or double it, I start each need for an external lib with the old crash then bash theory…

In the case I will show the google object that exists globally when you start playing with maps, autocomplete, places, bulk geo coding and so on, this is what I have done to make sure that my code wont cause any issue.



function allClearLetsGo(){
// your googlie bits go here. 

// eg. var googlieBit = new google.maps.places.Autocomplete([ELE, TYPES & RESTRICTIONS]);
// and on you go...
};



try {
 if (typeof google !== 'undefined'){  // if global google object isnt undefined
    allClearLetsGo(); // then run our function because we are good to go
  }else{ // BUT if it is undefined, append the script to the head, this will cause it to load.
    var key = 'MY-GOOGLE-API-KEY'; 
    $('head').append('<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=' + key + '&libraries=places&callback=allClearLetsGo"></script>');
// We use the same all clear function in the scripts callback param so once its loaded it then fires our code. 
  }
} catch (bugger) { // and if it all goes pair shaped we spit the error out in the console & hope its not our issue to sort out..
  console.log(bugger);
};


The google object I have left so broad is for a reason, its to show you can go about this fairly open or really specific depending on your needs.

Eg. we want to create the Autocomplete, so


if (typeof google.maps !== 'undefined'){
// your all clear code..
}

is a more specific check that the object google does in fact have the maps lib loaded, and meaning,


if (typeof google.maps.places !== 'undefined'){
// your all clear code..
}

is an even more specific check to see if the lib you need exists…

I have done more than enough now using the google object to vouch for this as pretty well fool proof… and if everyone went about something like this then who ever loads the lib first will be the only one that will need to…

hope this helps you guys out a bit.

1 Like

That’s a great write up of a sensible approach to the issue, thanks jarrad!

Sadly, for my particular case with the Places library, things are a bit trickier. The issue I’m facing is that Google, in their infinite wisdom, don’t provide any way to load only the Places library on its own. So if another element loads the Google Maps API without the libraries=places parameter (as the standard Bubble Maps element does), like:

<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=' + key + '&callback=allClearLetsGo"></script>

Then while this will be true:

if (typeof google.maps !== 'undefined'){
// your all clear code..
}

This will not be true:

if (typeof google.maps.places !== 'undefined'){
// your all clear code..
}

Unfortunately I can’t then just go and load the Places library on its own, instead I have to load the whole Google maps API again with the libraries=places parameter:

<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=' + key + '&libraries=places&callback=allClearLetsGo"></script>

Which then leads to things breaking. There’s been a request in Google’s issue tracker to allow loading libraries seperately since 2011, so I’m not holding out much hope of Google sorting it on their end.

More broadly, I can still imagine cases where checking if a global object already exists and then not loading it could cause issues- what happens if an element is loading an old version of a script where you need a more recent version for instance? It’d be lovely to have an elegant solution to this problem. Though I guess “elegant solution” and “javascript” don’t often go together :stuck_out_tongue:

Edit: I’ve found a hideously inelegant solution to my particular problem now. Basically copying and pasting the places function out of the Maps API and sticking it in my script when necessary. I don’t hold out much hope of this being particularly future-proof but it’ll do, just about.

I built the Autocomplete plugin based on this and what i did was put the allClearLetsGo() function inside an append to head function wrapped in script tags. The method works and has been tested using both bubble and non bubble maps and other elements both visable, hidden and some appended after the fact. This was what I came up with when bubble asked me to ensure that the google based plugins would be able to coexist with core elements and other devs elements aswell. I wish i could just chuck the code here but its a private version for a client so unfortunately i cant. The good news is though i can say 100% i understand the issue but more to the point 100% that its solvable this way.

even if you wanted to stick to this method, wrap it in the allClear function finish it with a return result, append it to the head making sure the var is global and you will be able to have the one block callable throughout your plugin as allClear. That way you only have it once but its reachable where ever you call it.

Interesting- do you not still get the

You have included the Google Maps API multiple times on this page. This may cause unexpected errors.

error in the console (when a standard bubble Maps element is on page, for eg)? Or is it just working fine despite this?

Thats the exact error that i needed to suppress and it works against that one.