JS : Sélection ou abandon de sélection d'un fichier

Il est parfois nécessaire de savoir si, après avoir ouvert un sélecteur de fichier, l'utilisateur d'une application web a validé une sélection ou l'a annulée...

La fenêtre du sélecteur de fichier n'est pas manipulable en javascript mais nous avons besoin d'être avertit de sa fermeture et si cette fermeture s'est faite par sélection de fichier ou par abandon de la sélection. L'astuce consiste à écouter un événement sur le document lors de l'ouverture - les mouvements de souris par exemple ; comme la fenêtre du sélecteur est modale, aucun événement n'est déclenché sur le document courant tant qu'elle reste ouverte. Quand elle se referme, les événements sont déclenchés ; il nous suffit alors d'aller voir le sélecteur de fichier pour savoir si un fichier a été sélectionné ou non...

/**
 *  Objet de gestion d'une élément d'upload de fichier en contrôlant
 *  la saisie de fichier et l'abandon.
 *  
 *  @param {HTMLInputElement} input Sélecteur de fichier à scruter.
 */
var Uploader = function(input)
{
    /**
     *  Sélecteur de fichier à gérer.
     *  
     *  @private
     *  @type HTMLInputElement
     */
    this.element = input;
    
    /**
     *  Ecouteur de mousemove
     *  
     *  @private
     *  @type Function
     */
    this.onMouseMoveBind = this.onMouseMove.bind(this);
    
    /**
     *  Flag indiquant si un fichier a été sélectionné.
     *  
     *  @private
     *  @type Boolean
     */
    this.changed = false;
    
    /**
     *  Flag indiquant si les résultats du gestionnaire sont en cours de
     *  traitement.
     *  
     *  @private
     *  @type Boolean
     */
    this.running = false;
    
    this.init();
};

Uploader.prototype = {
    // fix constructor
    constructor: Uploader,
    
    /**
     *  Initialisation. Attache des écouteurs d'événements.
     *  
     *  @private
     */
    init: function()
    {
        this.element.addEventListener('click', this.onClick.bind(this), false);
        this.element.addEventListener('change', this.onChange.bind(this), false);
    },
    
    /**
     *  Lancer la gestion de la sélection ou de l'abandon de sélection
     *  de fichier.
     *  
     *  @private
     */
    process: function()
    {
        if (this.changed) {
            // Do something on file selection
            console.log('file selected');
        } else {
            // Do something on cancel
            console.log('cancel');
        }
        this.running = false;
        this.changed = false;
    },
    
    /**
     *  Ecouteur d'événement change sur le sélecteur de fichier.
     *  
     *  @protected
     *  @param {Event} e DOM event
     */
    onChange: function(e)
    {
        if (this.running) {
            this.changed = true;
        }
    },
    
    /**
     *  Ecouteur de click sur le sélecteur de fichier.
     *  
     *  @protected
     *  @param {Event} e DOM event
     */
    onClick: function(e)
    {
        this.running = true;
        document.addEventListener('mousemove', this.onMouseMoveBind, false);
    },
    
    /**
     *  Ecouteur de mouvement de souris sur la page courante.
     *  
     *  @protected
     *  @param {Event} e DOM event
     */
    onMouseMove: function(e)
    {
        document.removeEventListener('mousemove', this.onMouseMoveBind, false);
        this.process();
    }
};
Uploader.js

Petite faiblesse de cette technique : il faut évidemment que l'utilisateur bouge la souris au-dessus du document courant pour que l'on soit avertit.

Démonstration