For independent forms JS, use independent ˂script˃ tags

Hey there, budding Forms 2.0 JS developer! Do you really know how local <script> tags work?  ’Cuz you should.

Let’s examine the difference between snippet A:

<script>
   // something you pasted from somewhere
   function ABC(){
     alerb("Independent function ABC running");
   }
   ABC();
  
   // something you pasted from somewhere else or wrote from scratch
   function DEF(){
     alert("Independent function DEF running");
   }
   DEF();
</script>

And snippet B:

<script>
   // something you pasted from somewhere
   function ABC(){
     alerb("Independent function ABC running");
   }
   ABC();
</script>

<script>   
   // something you pasted from somewhere else or wrote from scratch
   function DEF(){
     alert("Independent function DEF running");
   }
   DEF();
</script>

Yes, the typo in alerb is deliberate, and appears in both A and B. To save your eyesight: the only difference markup-wise is the number of <script> tags. In A, two functions are inside one <script>. In B, each function is in its own <script>.

The two functions are supposed to be independent: DEF doesn’t depend on ABC running first, nor vice versa. In real life, instead of just popping up alerts, they’d do stuff that’s totally different: ABC adds hidden form fields, while DEF plays a video. Or ABC sends a GTM event, while DEF initializes a chat widget.

So what’s the functional difference?

In A, a fatal ReferenceError is thrown as soon as ABC tries to run the nonexistent function alerb. And DEF never runs.

In B, a fatal ReferenceError is also thrown by ABC. But DEF runs successfully.

That’s a pretty big diff!

And in A, you’ve added an unnecessary chronological (OK, lexical if you want to get serious) dependency between the functions. If you switched the position of DEF and ABC — which might well happen accidentally while neatening up your code — then DEF would run. But they aren’t supposed to have any relationship at all.

By the way, if there’s a SyntaxError, it’s much less subtle. Here, neither function will run, regardless of order, because there’s a missing quotation mark:

<script>
   // something you pasted from somewhere
   function ABC(){
     aler("Independent function ABC running);
   }
   ABC();
  
   // something you pasted from somewhere else or wrote from scratch
   function DEF(){
     alert("Independent function DEF running");
   }
   DEF();
</script>

But again, if you separated them into two <script>s, only the bad function would break.

Why does this happen?

In HTML, a <script> element without a type or with one of the explicit JavaScript type values is interpreted as JS code.

(That’s obvious, perhaps, but classic scripts do differ from <script type="what/ever"> data blocks and the newer <script type="module">.)

All scripts have the same global environment and global object. But each script gets its own execution context. Therefore:

  • they do share any variables declared in their outermost scope or created directly on the window, but
  • they don’t share their final state (known formally as a Completion Record) which can either be normal or signify that a given script had a SyntaxError, ReferenceError, another abnormal exit

It’s a good balance, since well-behaved scripts can access each other’s data and methods, but badly-behaved scripts won’t have catastrophic side effects.

You may already be aware of this behavior for <script>s with remote src URLs. It’s why you can load a bunch of 3rd-party scripts from different origins and not worry about one broken script breaking all the others. But it’s not widely understood that it applies to local-only <script>s, too.

Use separate <script> tags when authors and/or objectives are different

Over the years, I’ve published a ton of Forms 2.0 JS snippets, from few-liners to complex enhancements spanning hundreds of lines. I’m overjoyed to see my open source code in the wild... but worry when I see snippets pasted back-to-back within the same <script>, let alone intermingled with other site code.

Now, if everything is thoroughly unit-tested, that’s fine. But let’s be honest, Marketo LPs are kind of the wild west and rarely even tested cross-browser. So dedicate a <script> to each discrete snippet whenever you can. You can call it Defensive Pasting™.