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.

Using CSS3 transforms and calc() in Internet Explorer

Although CSS calc sizes are supported in Internet Explorer 10 and above, they do not work when used in transform attributes. For instance, transform: translate(0, calc(100% - 10px)); does not work in Internet Explorer.

To work around this, you can chain transforms. For instance, the following statements are equivalent:

transform: translate(0, calc(100% - 10px)); //Does not work in IE
transform: translate(0, 100%) translate(0, -10px); //Works in IE

It’s that simple!

How to fix slow file read/write in Node.js

This morning, I had trouble with one of my grunt tasks taking several minutes to finish instead of 3-4 seconds. After a bit of troubleshooting, I isolated the bottleneck to a bunch of seemingly harmless file reads.

Guess who was the culprit? The company-supplied McAfee. Turning off on-access scan immediately fixed the issue and dropped the build time from 3 minutes to 5 seconds.

Django DeleteView with AJAX

The default Django generic DeleteView is not perfectly adapted for AJAX requests. A much simpler AjaxDeleteView can easily be implemented using the same mixins as Django’s generic class-based views:

class AjaxDeleteView(SingleObjectMixin, View):
    """
    Works like DeleteView, but without confirmation screens or a success_url.
    """
    def post(self, *args, **kwargs):
        self.object = self.get_object()
        self.object.delete()
        return HttpResponse(status=204)

This views performs CSRF validation just like the default DeleteView, except it won’t show any confirmation screens and has no need for the success_url attribute.

Return a user’s own objects with Django’s class-based views

There is a very simple way to only return the connected user’s own objects with Django’s generic class-based views.

When you extend get_queryset() on any view that implements SingleObjectMixin or MultipleObjectMixin (almost all of them), you can filter the default QuerySet to match your needs. It becomes fairly easy to create a mixin that filters any queryset to return objects created by the current user.

This is the mixin we will be using:

class OwnObjectsMixin():
    """
    Only returns objects that belongs to the current user. Assumes the object
    has a 'user' foreignkey to a User.
    """
    def get_queryset(self):
        user = self.request.user
        return super(OwnObjectsMixin, self).get_queryset().filter(user=user)

Let’s say you have a Bookmark model that has a foreign key called user that points to the standard User model. UserList view would look like this:

class UserList(OwnObjectsMixin, ListView):
    model = User

class UserDetails(OwnObjectsMixin, DetailView):
    model = User

You can use that mixin with CreateView, DeleteView and UpdateView too, making your views simple and maintainable.