Third-Party JavaScript — The Right Way
- JavaScript
- Frontend
- Web Development
Note: Published in 2017. While the core principles remain valid, web development has evolved. You can now leverage modern features like JavaScript modules to handle third-party scripts more efficiently.
When you work at a company like 1&1, a lot of the JavaScript you write doesn't run on pages you control. It runs on different websites, inside CMSs you've never seen, alongside other scripts you know nothing about. That changes what good code looks like.
The framing that helped me most: a website has two parties — the operator and the visitor. When you ship JavaScript that integrates into someone else's site (a social share button, an analytics snippet, an embeddable widget), you're the third party. Your code is a guest. Guests don't rearrange the furniture.
Most of what follows is obvious in retrospect, but I've seen each of these problems cause real damage in production. So here are the rules I kept coming back to.
Rule 0: It's not your page
Everything else is downstream of this one. You have no control over the site your code lands on. You don't know what else is running, what the DOM looks like, or how the developer who integrated you structured their HTML. You can't assume anything is where you expect it to be.
This sounds straightforward, but it changes the disposition you bring to every line of code. If a function might throw because of something on the host page, handle it. If a DOM node might be missing, don't assume it's there. If your script crashes, the whole page might crash with it — and that's a far worse outcome than your feature not loading.
Rule 1: No global variables
Global variables are shared state on the host page. Any name you claim is a name some other script — or the page itself — might also want to use.
The fix is simple: wrap everything in an IIFE and use local variables for everything.
(function(window, document, undefined) {
// all your code lives here
// local variables only
}(window, document))
This pattern gets you a clean local scope. window and document are passed in explicitly, which has the side benefit of making them local references — slightly faster to look up, and easier to minify. undefined is listed as a parameter but never passed an argument, which is the pre-ES5 trick for guaranteeing it actually means undefined in environments where it could theoretically be overwritten.
A linter like ESLint will catch accidental globals before they ship. Worth setting up from the start.
Rule 2: Use your own namespace
Sometimes you genuinely need to expose a public API — a function the integrating site can call to configure your widget, or an object they interact with. That means you need one global. One, not several.
Export everything as a single object with a name that's specific enough to be yours. Facebook uses FB. Twitter uses twttr. The naming logic isn't clever — it's just unlikely to collide with something else on the page. Your company name, your product name, something distinctive. Put all your public API surface on that one object and leave nothing else on window.
Rule 3: The DOM is not yours either
You can't trust the DOM structure of the host page. Not all real-world HTML is valid. Pages exist without <head> elements. Certain CMS setups produce structures you wouldn't believe until you've seen them in production logs.
This is the kind of assumption that will eventually burn you:
document.head.appendChild(myScript);
If there's no <head>, this throws. And when a third-party script throws at top level, bad things happen to the page that included it.
A more defensive approach for injecting a script:
<script>
(function() {
var s = document.createElement('script');
var g = document.getElementsByTagName('script')[0];
s.src = 'your-script.js';
s.type = 'text/javascript';
g.parentNode.insertBefore(s, g);
}());
</script>
This relies on one assumption: the page contains at least one script tag — specifically, the one that ran this code. That's a safe assumption. It finds the first script element and inserts your new node before it. No <head> required.
The same logic applies everywhere. Check before you access. Handle the null case. Don't let missing DOM nodes throw unhandled errors into the host page's console — or worse, abort execution.
And beyond robustness: don't touch the DOM unless you need to. Every modification you make to someone else's page is a potential interference.
Rule 4: Don't modify prototypes
Object.prototype, String.prototype, Array.prototype — these are shared. Every script on the page uses them. If you add a method to String.prototype because it's convenient, you've changed the behaviour of strings for every other script on that page. Including ones you've never heard of, built by people who had no idea you existed.
Even if your addition seems harmless, it creates the potential for naming collisions and unexpected behaviour in code you don't control. The rule is simple: don't do it. Utility functions belong in your local scope, not on global prototypes.
Rule 5: Don't bundle big libraries
Bundling jQuery — or any large library — into your third-party script creates two problems at once.
First, you're adding significant weight to the host page. If every third-party integration took this approach, the combined bundle size would be enormous. The people who integrate your script haven't agreed to that cost.
Second, and more insidiously: if the host page already uses jQuery, you now have two copies of jQuery on the same page. If they're different versions, the global $ might end up pointing to yours, not theirs — and their code breaks. Even if you scope your library properly, the interaction between two copies of a large library is a source of subtle, hard-to-diagnose bugs.
Plain JavaScript solves both problems. Browser APIs have come a long way. Most of what you'd reach for a library to do, you can do without one. If you genuinely can't avoid a dependency, at minimum ensure it runs in a contained scope and doesn't leak globals.
The thread running through all of this is the same as Rule 0: you're a guest. Your code has to work in environments you haven't tested, alongside scripts you've never seen, on pages that may not be well-formed. The measure of a good third-party integration isn't just that it works — it's that it doesn't break anything that was working before you arrived.