Comment créer une zone de scroll sans scroll barre ?

Voici un problème auquel j'ai été confronté lors de la création d'un site
qui sera très prochainement présenté sur mon portfolio.
Comment créer plusieurs zones scrollables, responsives et compatibles avec les téléphones et tablettes
sans avoir des scroll barres qui se balladant dans tous les sens ?

N'étant pas certain que le cheminement de mon raisonnement intéresse l'ensemble des lecteurs,
je mets le code complet de mon objet à la fin de cet article.

J'ai commencé par développer un objet jQuery qui me structure la zone :
var scroller = {
	$Wrap: null,
	$Inner: null,
	marge: 20, //Cette marge permet un padding bottom plus esthétique
	init: function(id)
	{
		// Je donne la possibilité de passer en argument un objet ou une string
		var id = (typeof id == 'object') ? id.attr('id') : id;

		var that = this;
		// j'instancie mon conteneur
		this.$Wrap = $('#'+id);

		// Je créais mon contenant
		var tmpInner = $('<div class="clearfix" id="'+id+'-inner" />');
		this.$Wrap.wrapInner(tmpInner);
		this.$Inner = $('#'+id+'-inner');
	}
}

Il faut maintenant ajouter la fonction scroll.
Pour cela, j'ai d'abord mis en place une fonction utilisant un écouteur de type

wrap.addEventListener('mousewheel', ... 

avec les conditions nécessaires aux différents types de navigateur.

Cette solution pausait deux problèmes :

  1. Si la zone ne nécessite pas de scroll, le défilement de la page est tout de même ralenti sur cette zone car le navigateur exécute le défilement à la vitesse de la fonction et non plus à celle par défaut du navigateur.
  2. Sur mobile on ne scroll pas on swipe, de fait mon event scroll n'est pas pris en charge et comme la zone à scroller contient des blocs, la méthode "on('swipe', ...) de jquery mobile ne fonctionne pas correctement et retourne un drag au lieu d'un swipe. .
La solution

J'ai donc choisi de retourner aux anciennes méthodes avec une scroll barre cachée.
L'idée est simple : le conteneur principal (wrap) est en overflow : hidden et le contenant (que j'appelle inner) et en overflow : scroll, ou plus précisément en overflow-y : scroll.
Ensuite je décale vers la droite le contenant de la largeur de la scroll barre.

Si vous mettez en overflow : auto, vous êtes obligé de mettre le décalage à droite seulement lorsque la scrollbarre apparait.
Le fait de mettre overflow : scroll permet de toujours avoir la scroll barre et donc de toujours décaler vers la droite (cela économise une fonction).

Le code complet
var scroller = {
	$Wrap: null,
	$Inner: null,
	marge: 20, //Cette marge permet un padding bottom plus esthétique
	init: function(id)
	{
		// Je donne la possibilité de passer en argument un objet ou une string
		var id = (typeof id == 'object') ? id.attr('id') : id;

		var that = this;
		// j'instancie mon conteneur
		this.$Wrap = $('#'+id);

		// Je créais mon contenant
		var tmpInner = $('<div id="'+id+'-inner" class="clearfix" />');
		this.$Wrap.wrapInner(tmpInner);
		this.$Inner = $('#'+id+'-inner');

		// apple a son propre fonctionnement de scroll barre et ne tien pas compte de certains comportements
		var is_apple =  (/iPad/.test(navigator.platform) || /iPhone/.test(navigator.platform) || /MacIntel/.test(navigator.platform));

		// Je définie le CSS
		/* scroll avec css / html */
		this.$Wrap.css({
			'overflow':'hidden'
		});
		this.$Inner
			.append('<div style="width:100%; height:'+this.marge+'px"></div>')
			.css({
				'float': 'right',
				'overflow':'hidden',
				'overflow-y': ( is_apple ? 'auto' : 'scroll'),
				'width': '100%',
				'height': '100%',
				'margin-right' : ( is_apple ? 0 : -17 )
			});

	}
}

Pour déclarer l'objet sur un élément html on utilise ceci :

var nav_scroll = $.extend({},scroller);
    nav_scroll.init('nav');

'nav' étant le conteneur auquel on souhaite ajouter le scroll.
Pour ceux qui on suivi, 'nav' peut être une string (comme ci-dessus) ou un objet comme par exemple :

var nav_scroll = $.extend({},scroller);
    nav_scroll.init($('#nav'));
 Enjoy ! :)