惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
L
LINUX DO - 最新话题
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
Know Your Adversary
Know Your Adversary
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
L
Lohrmann on Cybersecurity
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Security Latest
Security Latest
T
The Exploit Database - CXSecurity.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy & Cybersecurity Law Blog
K
Kaspersky official blog
The Last Watchdog
The Last Watchdog
Webroot Blog
Webroot Blog
Scott Helme
Scott Helme
T
Threat Research - Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
WordPress大学
WordPress大学
L
LINUX DO - 热门话题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - Franky
V
Visual Studio Blog
O
OpenAI News
AI
AI
Hacker News: Ask HN
Hacker News: Ask HN
V2EX - 技术
V2EX - 技术
GbyAI
GbyAI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Simon Willison's Weblog
Simon Willison's Weblog
S
Schneier on Security
Spread Privacy
Spread Privacy
Y
Y Combinator Blog
I
InfoQ
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
F
Fortinet All Blogs
C
CERT Recently Published Vulnerability Notes
T
The Blog of Author Tim Ferriss
C
Check Point Blog
Apple Machine Learning Research
Apple Machine Learning Research
有赞技术团队
有赞技术团队
人人都是产品经理
人人都是产品经理
N
News and Events Feed by Topic
Project Zero
Project Zero
小众软件
小众软件
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
B
Blog
G
Google Developers Blog

Philip Walton

The State of ES5 on the Web Dynamic LCP Priority: Learning from Past Visits Performant A/B Testing with Cloudflare Workers My Challenge to the Web Performance Community Smaller HTML Payloads with Service Workers Cascading Cache Invalidation Using Native JavaScript Modules in Production Today KV Storage: the Web's First Built-in Module Idle Until Urgent Page Lifecycle API First Input Delay Responsive Components: a Solution to the Container Queries Problem Why Web Developers Need to Care about Interactivity Deploying ES2015+ Code in Production Today How We Track Pageviews Is All Wrong The Google Analytics Setup I Use on Every Site I Build The Dark Side of Polyfilling CSS Loading Polyfills Only When Needed Untangling Deeply-Nested Promise Chains Learning How to Set Up Automated, Cross-browser JavaScript Unit Testing Houdini: Maybe the Most Exciting Development in CSS You've Never Heard Of Why I'm Excited About Native CSS Variables Do We Actually Need Specificity In CSS? How to Become a Great Front-End Engineer Extending Styles Side Effects in CSS Normalizing Cross-browser Flexbox Bugs Measuring Your Site's Responsive Breakpoint Usage The Dangers of Stopping Event Propagation Implementing Private and Protected Members in JavaScript How to Find Qualified Developers Interviewing as a Front-End Engineer in San Francisco Solved by Flexbox Decoupling Your HTML, CSS, and JavaScript Why I Test Private Functions In JavaScript How to Unit Test Private Functions in JavaScript Introducing HTML Inspector CSS: Everything is Global and How to Deal With It Dynamic Selectors Defending Presentational Class Names The Future of OOCSS: A Proposal What No One Told You About Z-Index CSS Architecture
Stop Copying Social Code Snippets
2014-04-21 · via Philip Walton

If you’re a web developer, chances are, at some point, you’ve had to add a social widget to one of your page. Actually, probably more than one. And chances are you didn’t want to put too much thought into it, so you just copied some code from the Internet and called it a day. Done and done.

I mean, the Web is filled with code that has been blindly copied from one source to another. Tweet buttons, Like buttons, Disqus comments, Google Analytics, all of these come with nice, neat, prepackaged code snippets.

In case you haven’t figured it out from the title (and my tone), I think using these snippets is a bad idea.

If you’re a professional web developer, these snippets are not for you. They’re for your friends and relatives, your uncle’s restaurant business or your sister’s baby blog, but they’re not for you.

One size fits all solutions can never been optimized for all cases, and they frequently come with baggage you don’t want or need.

If you care about performance, reusability, and clean code, then you should make an effort to avoid this temptation. Rise to the challenge, as a professional, and take the two seconds it requires to figure out what the code is doing. Maybe you don’t actually need it, or maybe you can do it better.

Now, you might be thinking to yourself: “but this code was written by a super genius at [insert big company name here] and its been tested on millions and millions of websites; I don’t want to mess with that.”

The first part of this sentiment is absolutely not true. The people who wrote these snippets are just like you and me, and the complexity of the code is so minimal that the intelligence of the author is irrelevant. In any case, the source is right there, so you can learn from their genius.

The second part of this sentiment (the testing part) may be a legitimate concern in general, but I don’t think it really applies to social widget code. These snippets are only a couple of lines at most, and usually, either they work or they don’t. If you don’t see any JavaScript errors in the console, and the widgets actually load in the browsers you’re targeting, that’s pretty much all the testing you need.

Note that this doesn’t actually require additional testing on your part since you should be checking that these widgets work in your target browsers anyway. There’s always the possibility that something you’re doing is conflicting with the recommended snippets, so regardless of what you do, you still have to test that it works.

Almost every social snippet out there does the same two things:

  1. It adds a placeholder element to the HTML.
  2. It loads a script from their site that adds some stuff to the placeholder.

That’s it. Don’t believe me? Well, let’s look at a few examples.

(If you do believe me and just want to skip to my recommendations, go to the section entitled What To Do Instead.)

Twitter

Here is the code that developers.twitter.com recommends you use to get a tweet button on your site.

<a href="http://example.com" class="twitter-share-button" data-lang="en">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

Don’t let the fact that it’s all minified and obfuscated intimidate you. This is very basic code that I’m confident you can understand.

The HTML part is pretty straight forward. It’s just a plain old link with the class twitter-share-button. This is the placeholder element that I mentioned above.

The JavaScript portion is a bit more complex, so let me prettify the code a bit and replace the single letter variables so you can get a clearer picture of what’s going on.

var js, fjs = document.getElementsByTagName('script')[0];
if (!document.getElementById('twitter-wjs')) {
  js = document.createElement('script');
  js.id = 'twitter-wjs';
  js.src = 'https://platform.twitter.com/widgets.js';
  fjs.parentNode.insertBefore(js, fjs);
}

Let’s walk through what this code is doing.

  1. It declares the variables js and fjs, and assigns fjs to the first script element on the page.
  2. It then checks to see if the document already has an element with the ID “twitter-wjs”. If it does, nothing happens. If it doesn’t, it goes into the if conditional.
  3. It creates a new script element, assigns it to the js variable, and sets the ID to twitter-wjs and the src attribute to https://platform.twitter.com/widgets.js.
  4. Finally, it inserts the script element into the DOM immediately before the first script element.

This is actually very uncomplicated code. All it’s doing is downloading an external script and running it on your page.

It does a single check to make sure the script hasn’t already been downloaded, which is important because so many people blindly paste this code multiple times onto the same page, and obviously the same script file doesn’t need to be downloaded more than once.

Well, that was Twitter, now let’s take a look at another snippet.

Facebook

<div id="fb-root"></div>
<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/en_US/all.js";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>

<!-- then put the share button somewhere -->
<div class="fb-share-button" data-href="http://example.com" data-type="button_count"></div>

Facebook’s code is a little less obfuscated, so this snippet is a bit easier to read. Can you see how similar it is? They even use the exact same variable names: d, s, and id, js, and fjs.

<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_shortname = ''; // required: replace example with your forum shortname
(function() {
var dsq = document.createElement('script');
dsq.type = 'text/javascript';
dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
</script>

The Disqus snippet is slightly different because it requires you to assign your Disqus username to the variable disqus_shortname and then it downloads a script from that subdomain. But there are also difference in how it loads the external script:

  • It adds the type attribute “text/javascript” to the script element.
  • It adds the async attribute.
  • It appends the newly created script element to the <head> (or to the <body> if <head> can’t be found) rather that inserting it before the first script element.

You might be asking yourself. Are these differences important? Disqus is a big name company, surely they’re doing things differently for a reason, right?

To answer this question bluntly: no, these differences are not important.

First of all, it’s totally unnecessary to set a script’s type attribute to “text/javascript” if you’re using an html5 doctype (which you probably are). Secondly, it’s unnecessary to check for the presence of the <head> element since all browsers automatically add that when creating the DOM (even if it’s not present in the source). Finally, the async attribute is added to the script to prevent it from blocking during download; however, any script that is dynamically added to the DOM is evaluated as async by default.

I’m going to give Disqus the benefit of the doubt and assume that they had a good reason for writing the code they did. Perhaps they had some big enterprise client with really old, invalid HTML, parsing errors, and no doctype, and this was the solution they needed to get it working for them. I don’t know, but the point is these differences don’t apply to you, so there’s no reason to carry someone else’s baggage.

This also helps disprove the first myth about social snippets. Just because some big name company is doing it a certain way, doesn’t mean that’s a best-practice.

Google Analytics

Google Analytics is a little bit more complicated than the other social snippets. In addition to downloading an external script, it also creates a function on the page that the external script needs to interact with.

Here’s the Google Analytics snippet, formatted so it’s a bit easier to read:

(function(i, s, o, g, r, a, m) {
  i['GoogleAnalyticsObject'] = r;
  i[r] = i[r] || function() {
    (i[r].q = i[r].q || []).push(arguments)
  }, i[r].l = 1 * new Date();
  a = s.createElement(o),
  m = s.getElementsByTagName(o)[0];
  a.async = 1;
  a.src = g;
  m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

ga('create', 'UA-XXXX-Y', 'auto');
ga('send', 'pageview');

While this code is definitely more involved than the other snippets, and changing it will increase the risk of things breaking. I’d still recommend you don’t blindly paste it into your website.

The Google Analytics developer documentation has an unminified and commented version of this script, and I highly suggest looking at that to see what’s actually going on.

To spoil the mystery, unless you’re doing something custom, my version below will work just fine:

ga = function() {
  ga.q.push(arguments);
};
ga.q = [['create', 'UA-XXXX-Y', 'auto'], ['send', 'pageview']];
ga.l = 1 * new Date();

This updated snippet creates a ga function that, when invoked, pushes the arguments passed onto its q property. It then initializes the q property with your tracker data and stores the current time on the l property.

This new code does the exact same thing as the original snippet, but it’s vastly simplified since it doesn’t try to support all possible custom use cases.

All you have to do is load the external script yourself, and everything should work as normal.

What To Do Instead

While using asynchronous script loading is certainly better than a bunch of blocking scripts littered throughout the page, its no magic bullet. Asynchronous scripts don’t block rendering, but they do delay page loads.

If you add a script element to the DOM before the window’s load event fires, that event will be delayed until the newly added script is downloaded, parsed, and run. If you’re adding multiple scripts (most people are), the load event could be delayed a long time.

Now, you might be thinking to yourself. Who cares about load? Isn’t DOMContentLoaded the event I should really be worried about? Isn’t that what we learned from jQuery?

DOMContentLoaded is the event that fires once the document has been fully parsed and is able to be traversed by scripts. If you’re waiting to do some DOM manipulation, then yes, this is the event you should care about.

But the window’s load event is just as important, arguably more so, and for a few reasons:

  • Google search ranking algorithms take page load times into account, and those page load times are based on the load event.
  • If the URL has a hash fragment, browsers will not scroll down to that location until after the load event fires. This can significantly affect how snappy your page feels.
  • Browser plugins commonly listen for the load event, so any plugins your visitors have could make your pages seem slower than they really are.

I’m sure there are many more reasons to care about the load event, but this list is convincing enough for me.

Given all of this, you have two choices for how to implement your social widgets. And your decision should depend solely on how essential these widgets are to your user experience:

  • If they’re essential, they shouldn’t be asychronously loaded at all.
  • If they’re non-essential (which is usually the case), you should wait until after the load event to download and intialize them.

Waiting Until After Page Load

Since none of the snippets we’ve look at thus far wait for the load event, we’ll have to implement our own script loader. This is easy as we’ve already seen many examples for how to do it. Here’s a basic implementation:

var getScript = (function(firstScript) {
  return function(src) {
    var script = document.createElement('script');
    script.src = src;
    firstScript.parentNode.insertBefore(script, firstScript);
  }
}(document.getElementsByTagName('script')[0]));

The above code caches a reference to the first script on the page (to avoid multiple DOM lookups), then it returns a function that adds your new script element right before the first script.

Once you have a script loading function, just use it in the window.onload callback.

window.onload = function() {
  getScript('//www.google-analytics.com/analytics.js');
  getScript('//philipwalton.disqus.com/embed.js');
  getScript('https://platform.twitter.com/widgets.js');
  getScript('//connect.facebook.net/en_US/all.js');
};

If you’re using a library like jQuery, then you don’t even need to write the script loading code at all. jQuery can do that for you. (As can many other libraries, so check your dependencies before reinventing the wheel.)

$(window).on('load', function() {
  $.getScript('//www.google-analytics.com/analytics.js');
  $.getScript('//philipwalton.disqus.com/embed.js');
  $.getScript('https://platform.twitter.com/widgets.js');
  $.getScript('//connect.facebook.net/en_US/all.js');
});

It’s important to note that some of these social snippets (like Google Analytics) still require initialization, so don’t forget about those parts.

Bundling the External Scripts With Your Main JavaScript File

If one or more of these third party widgets are critical to your user experience, then you should be loading them immediately, not asynchronously.

To do this you’ll need to add a step to your build process that downloads these external dependencies, bundles them with your other scripts, and produces a single, minified JavaScript file that is loaded immediately preceding the closing </body> tag.

In many ways, downloading and serving these social scripts yourself is safer and more resilient than loading them on the fly from a domain you don’t control. In fact, you could even take this approach if you’re loading these scripts after the load event. Bundling them together reduces the total number of requests and it allows you to control exactly what code (and what version of that code) is being run.

On the other hand, if your users have those social scripts cached (as many do) bundling them will negate that performance benefit. You’ll have to weigh the pros and cons of each approach for yourself.

Conclusion

I know that copying and pasting social code is dead simple. And I know that trying to figure out what each snippet is doing (and then optimizing it for your specific needs) will take time. But I hope this article gives you the courage and the motivation to try. At the very least, if you’re going to use someone’s pre-packaged code, you should have a good reason.

In addition, I hope that more companies will follow the example set by Google Analytics and post an unminified, commented version of their snippets on their developer sites. There’s no reason to force users into the lowest common denominator. Give your non-savvy users the simplest option, but don’t assume your more technical users aren’t capable of customizing the code to fit their needs.

Additional Resources