How to make sure your og:image is used by Facebook

You can define custom meta tags on your website to decide it will appear when shared on Facebook. One of them is the og:image tag.

Unfortunately, it does not work reliably, and even their Open Graph debugger won’t help you. For instance, I got the following message:

og:image should be larger: Provided og:image is not big enough. Please use an image that’s at least 200×200 and preferably 1500×1500. (Maximum image size is 5MB.) Image ‘url from image, size: 300x443px 97kb’ will be used instead.

My image fit all the criteria and even showed in the og:image preview, yet Facebook refused to use it. It turns out that EXIF data was the culprit. Stripping the image of EXIF data and reuploading it solved the problem. This odd solution is supported by members of various communities.

If you are using WordPress, you can use plugins to set Facebook meta tags and automatically strip images of EXIF data, but there is no plugin that lets you strip EXIF data only for featured images.

How to fix “Cannot read property ‘failedExpectations’ of undefined”

While running Karma unit tests, you might run into the following error: Uncaught TypeError: Cannot read property 'failedExpectations' of undefined. The cryptic error message might be a pain in the butt to debug, so let me save you some time.

In your tests, look for reassignment of the result variable. You are probably overwriting a variable from Karma, and this is what breaks your tests. In my case, I was assigning this.result in beforeEach, and renaming it to this.promiseResult fixed the error.

Deleting directories while preserving symlinks

At work, we needed to replace a pre-existing folder with a symlink. We wanted to symlink the user’s .git/hooks folder to ../build/git-hooks every time the script was run.

We wanted to delete .git/hooks if it existed and replace it with a symlink, but we also wanted to avoid deleting ../build/git-hooks‘s contents by accident if the symlink already existed.

Using rm .git/hooks was not possible. If .git/hooks was already a symlink, it would be deleted and we could simply recreate it. Perfect! However, if .git/hooks was a non-empty directory (it contains examples by default), it would not be deleted, and the symlink could not be created.

Using rm -r .git/hooks was also impossible. If .git/hooks was already symlinked, the -r flag would delete the contents of ../build/git-hooks.

This is how we solved the problem:

#!/bin/bash
set -e  # Script exits with 1 on error

git_hooks_dir=".git/hooks"
link_to="../build/git-hooks"

# Safely delete the folder or symlink
if [[ -L $git_hooks_dir ]];
then
    # Folder already symlinked. Recreate symlink in case the directory changed.
    rm -f $git_hooks_dir
else
    # Possibly an existing, non-empty folder. 
    rm -rf $git_hooks_dir
fi

# Create the symlink
ln -sf $link_to $git_hooks_dir

How to throttle scroll events in AngularJS

If you watch the scroll event, you will probably find yourself handling far more events than you need. The scroll event fires really quickly, and that can be a problem on mobile devices.

The following Angular directive will call a specified scroll event every 250 milliseconds.

angular.module('hereApp.directive').directive('onScroll', function($timeout) {
    'use strict';

    return {
        scope: {
            onScroll: '&onScroll',
        },
        link: function(scope, element) {
            var scrollDelay = 250,
                scrollThrottleTimeout,
                throttled = false,
                scrollHandler = function() {
                    if (!throttled) {
                        scope.onScroll();
                        throttled = true;
                        scrollThrottleTimeout = $timeout(function(){
                            throttled = false;
                        }, scrollDelay);
                    }
                };

            element.on("scroll", scrollHandler);

            scope.$on('$destroy', function() {
                element.off('scroll', scrollHandler);
            });
        }
    };
});

The scrollable element should look like this:

<div on-scroll="myScrollEvent()"></div>

Disable “pull to refresh” in Chrome for Android

In mobile Chrome, pulling the top of the page will reload it. Although it’s a convenient UI pattern, it might get in the way if you are trying to implement gestures in your app.

After careful observation, I have come up with a simple solution.

AngularJS

I have successfully disabled “pull to reload” with this AngularJS directive:

//Prevents "pull to reload" behaviour in Chrome. Assign to child scrollable elements.
angular.module('hereApp.directive').directive('noPullToReload', function() {
    'use strict';

    return {
        link: function(scope, element) {
            var initialY = null,
                previousY = null,
                bindScrollEvent = function(e){
                    previousY = initialY = e.touches[0].clientY;

                    // Pull to reload won't be activated if the element is not initially at scrollTop === 0
                    if(element[0].scrollTop <= 0){
                        element.on("touchmove", blockScroll);
                    }
                },
                blockScroll = function(e){
                    if(previousY && previousY < e.touches[0].clientY){ //Scrolling up
                        e.preventDefault();
                    }
                    else if(initialY >= e.touches[0].clientY){ //Scrolling down
                        //As soon as you scroll down, there is no risk of pulling to reload
                        element.off("touchmove", blockScroll);
                    }
                    previousY = e.touches[0].clientY;
                },
                unbindScrollEvent = function(e){
                    element.off("touchmove", blockScroll);
                };
            element.on("touchstart", bindScrollEvent);
            element.on("touchend", unbindScrollEvent);
        }
    };
});

It’s safe to stop watching as soon as the user scrolls down, as the pull to refresh has no chance of being triggered.

Likewise, if scrolltop > 0, the event won’t be triggered. In my implementation, I bind the touchmove event on touchstart, only if scrollTop <= 0. I unbind it as soon as the user scrolls down (initialY >= e.touches[0].clientY). If the user scrolls up (previousY < e.touches[0].clientY), then I call preventDefault().

This saves us from watching scroll events needlessly, yet blocks overscrolling, and thus pulling to reload.

jQuery

If you are using jQuery, this is the untested equivalent. element is a jQuery element:

var initialY = null,
    previousY = null,
    bindScrollEvent = function(e){
        previousY = initialY = e.touches[0].clientY;

        // Pull to reload won't be activated if the element is not initially at scrollTop === 0
        if(element[0].scrollTop <= 0){
            element.on("touchmove", blockScroll);
        }
    },
    blockScroll = function(e){
        if(previousY && previousY < e.touches[0].clientY){ //Scrolling up
            e.preventDefault();
        }
        else if(initialY >= e.touches[0].clientY){ //Scrolling down
            //As soon as you scroll down, there is no risk of pulling to reload
            element.off("touchmove");
        }
        previousY = e.touches[0].clientY;
    },
    unbindScrollEvent = function(e){
        element.off("touchmove");
    };
element.on("touchstart", bindScrollEvent);
element.on("touchend", unbindScrollEvent);

Naturally, the same can also be achieved with pure JS.

How to replace accented characters in a Javascript string

If you are dealing with international user, you will sometimes need to replace unicode characters (éåü) with their ascii counterparts (eau).

The easiest way is to use a small library called stringops. It extends the String prototype to give your strings the .noAccents method. For instance, you can do "éåü".noAccents() and you will get "eau".

The library is very lightweight, but features other useful utilities to deal with strings. The full documentation is available on its github page, along with examples.

Multiple git stashes

Did you know git can keep multiple stashes? If you use git stash and git stash apply, you might be tempted to think there is a single stash, but you can restore older stashes, and thus stash multiple items.

To see your previous stashes, use git stash list. You will get a result like this:

stash@{0}: WIP on master: 6c483e2 Updated location.search to use empty string instead of null
stash@{1}: WIP on master: 6c483e2 Updated location.search to use empty string instead of null
stash@{2}: WIP on master: 142e661 Removed middle state for the search results panel
stash@{3}: WIP on master: 0e65041 Added spinner to search
stash@{4}: WIP on master: 723eef5 Put the search query in the search service
...

To restore a particular stash, use git stash apply stash@{0} and replace 0 with the index of the stash you wish to restore.

Use git stash clear to erase all your stashed changes.

input.select() does not work on iOS

The recommended method to give a text field focus and select its contents is the following:

document.getElementById('myInput').select();

However, this does not work in iOS. The correct way to do it is to use setSelectionRange on an already focused input.

var input = document.getElementById('myInput');
input.focus();
input.setSelectionRange(0,99999);

setSelectionRange will not work on iOS if the element doesn’t have focus. Also be advised that anything revolving around giving text inputs focus is highly unreliable on mobile Safari.