Enabling embedded Tweets

I made some updates to my blog to enable embedding Tweets and other widgets in the article contents.

A while ago I was drafting an article in which I thought it would be nice to include some relevant Tweets. It didn't just work out of the box because of the order of operations to turn the embedded Tweet markup is important. I use Angular, so the content of each article is fetched, and then the DOM is updated to include the content. In order for the Tweet markup that is in the article content to get rendered as a nice Tweet instead of a blockquote, the Twitter widget javascript has to be told to scan the DOM (or a subset of the DOM) for widgets to process.

This is the code you would put in the article content in order to embed a Tweet:

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">⚡ Homebrew upgrade  by <a href="https://twitter.com/beamjack">@beamjack</a><a href="https://t.co/9YgJghYxiZ">https://t.co/9YgJghYxiZ</a></p>&mdash; Lee Adams🎒 (@beamjack) <a href="https://twitter.com/beamjack/status/838437340477444096">March 5, 2017</a></blockquote>

Note that I remove this line from Twitter's default generated code:

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

This is because I may embed multiple Tweets and I don't want to execute that script for each one. I am going to load the widgets javascript once in my page template and then trigger it to scan for widgets after the article content loads. It can be found here.

    <script>window.twttr = (function(d, s, id) {
      var js, fjs = d.getElementsByTagName(s)[0],
      t = window.twttr | {};
      if (d.getElementById(id)) return t;
      js = d.createElement(s);
      js.id = id;
      js.src = "https://platform.twitter.com/widgets.js";
      fjs.parentNode.insertBefore(js, fjs);

      t._e = [];
      t.ready = function(f) {
      t._e.push(f);
      };

      return t;
      }(document, "script", "twitter-wjs"));
    </script>

My blog was already running highlightjs on the content in order to colorize code blocks, so I just needed to update the directive that does that to also run the Twitter code:

    .directive('highlight', function () {
        return {
            replace: false,
            scope: {
                'ngBindHtml': '='
            },
            link: function (scope, element, attrs) {
                scope.$watch('ngBindHtml', function(newValue, oldValue) {
                    element.html(newValue);
                    var items = element[0].querySelectorAll('code,pre');
                    angular.forEach(items, function (item) {
                        hljs.highlightBlock(item);
                    });
                });
            },
        };
    })

becomes more general as:

    .directive('articleContent', function () {
        return {
            replace: false,
            scope: {
                'ngBindHtml': '='
            },
            link: function (scope, element, attrs) {
                scope.$watch('ngBindHtml', function(newValue, oldValue) {
                    element.html(newValue);
                    var items = element[0].querySelectorAll('code,pre');
                    angular.forEach(items, function (item) {
                        hljs.highlightBlock(item);
                    });
                    twttr.widgets.load(element);
                });
            },
        };
    })

Edit (12/11/2017): I made a small but important improvement by utilizing the Twitter script's ready function:

twttr.ready(function() {
    twttr.widgets.load(element);
}

And now I can do this: