- Blog Xebia France - http://blog.xebia.fr -
Composant Scroll Avec jQuery
Posted By Amin Fathallah On Mercredi 30 novembre 2011 @ 8:21 In Java / JEE,RIA | 11 Comments
Dernièrement, j’ai participé au développement d’un Framework de composants visuels en HTML, jQuery et Java. Parmi ces composants, nous avons mis en place une grille de données évoluée avec des fonctionnalités étendues (Support du drag and drop, rafraichissement partiel de la page, …). Ce composant est intégré dans une application web utilisée la majorité de temps depuis un terminal tactile. Pour naviguer à travers les données, la grille de données utilisait un contrôle de pagination classique dans le style ci-dessous :

Ce type de pagination contraint l’utilisateur à effectuer plusieurs clics pour avancer à travers les données paginées, et atteindre sa page cible. Pour améliorer ce comportement, un ergonome et moi avons proposé une nouvelle stratégie de pagination permettant d’atteindre la page souhaitée avec un minimum de clics, tout en gardant un comportement proche d’une pagination classique (pagination des pages). Le résultat de notre travail est un contrôle scroll permettant de paginer à travers les données en utilisant la fonctionnalité drag-and-drop.
L’expérience tactile des utilisateurs et la résolution fixe du terminal nous imposait certaines règles à respecter pour une interaction facile et aisée avec l’application :
Une fois le travail réalisé, j’ai trouvé intéressante l’idée de partager cette expérience de code avec des personnes sensibles à l’ergonomie et à l’utilisabilité des interfaces web.
Le contrôle Scroll est sensible aux évènements suivants : click, drag-and-drop, et mouse-wheel. Il est composé essentiellement des éléments suivants :
Les boutons de navigation sont des boutons cliquables qui par souci de design ont la même hauteur qu’une ligne de données. L’espace de scrolling est défini sur la base de nombre de lignes maximum par page. La barre de scroll est un bloc graphique draggable dans l’espace de scrolling. La taille de ce bloc dépend du nombre total de pages remontées par le serveur et de la contrainte de taille des doigts des utilisateurs de l’application.
Les évènements click et mouse-wheel sont branchés sur l’espace scrolling pour permettre une meilleure expérience utilisateur. L’évènement drag-and-drop est branché sur la barre de scroll pour naviguer à travers les données en utilisant cette partie du composant.
La barre de scroll peut avoir une taille dynamique seulement si le nombre total de pages est inférieur à 6 (valeur définie dans mon exemple et correspondant à la résolution maximale utilisée par mon application, vous pouvez redéfinir cette valeur selon vos spécifications). Dans ce cas la taille de la barre de scroll est générée par une formule de calcul et dépend de la taille de l’espace de scrolling et du nombre de pages.

Si le nombre total de pages est supérieur ou égal à 6, la barre de scroll générée par la formule de calcul devient trop petite pour les doigts de l’utilisateur. J’utilise alors une formule de calcul pour générer une barre de scroll à taille fixe quelque soit le nombre de page.

Pour une meilleure intégration avec les styles CSS, la structure HTML du composant scroll est basée essentiellement sur des balises DIV. Les identifiants avec marqueurs sont nécessaires pour intégrer plusieurs instances du composant scroll dans la même page. Les classes CSS définissent les styles, et le positionnement des éléments dans la page.
<!--Le composant scroll-->
<div id="scrollComponent#{marker}" class="scrollComponent">
<!--Bouton de navigation (Reculer d’une page)-->
<div id="scrollUpButton#{marker}" class="scrollUpButton"></div>
<!--Espace de scrolling-->
<div id="scrollBarContainer#{marker}" class="scrollBarContainer">
<!--Barre de scroll-->
<div id="scrollBar#{marker}" class="scrollBar">
<label id="pageNumberLabel#{marker}" class="pageNumberLabel"></label>
</div>
</div>
<!--Bouton de navigation (Avancer d’une page)-->
<div id="scrollBottomButton#{marker}" class="scrollBottomButton"></div>
</div>
Les styles CSS permettent de donner l’aspect visuel du composant Scroll. Le JavaScript est nécessaire pour calculer les dimensions, positionner et afficher les différentes briques du composant scroll en fonction du nombre des pages et des paramètres avancés par l’application.
Le style appliqué permet de définir la largeur du composant scroll. Il définit également l’image de fond, et les images des boutons de navigation dans les données et leurs positions.
.scrollComponent {
width:46px;
background-image: url("./images/scrollup.png"), url("./images/scrolldown.png"), url("./images/contenu.png");
background-repeat: no-repeat, no-repeat, repeat-y;
background-position: center top, center bottom, center top;
}
Le style appliqué permet de définir la largeur de l’espace de scrolling. La hauteur est calculée en fonction du nombre des lignes maximum à afficher par page.
.scrollBarContainer {
width:46px;
}
La classe CSS scrollBar permet de définir la largeur de la barre du scroll, son positionnement, ses bordures, et son image de fond. La taille de la barre de scroll est calculée selon le nombre de pages. La classe CSS pageNumberLabel permet de définir le positionnement, les propriétés de la police des caractères, et la largeur du libellé utilisé pour l’affichage de la page en cours.
.scrollBar {
position:absolute;
width:39px;
margin-left:4px;
-webkit-border-radius:150px;
-moz-border-radius:150px;
-o-border-radius:150px;
border-radius:150px;
background-image:url("./images/texture.png");
}
.pageNumberLabel {
position:absolute;
top:40%;
width:37px;
font-family:Trebuchet MS,Arial,Helvetica,Jamrul,sans-serif;
font-size:9px;
color: #FFFFFF;
font-weight: bold;
text-align: center;
}
Le style appliqué sur les boutons de navigation permet de définir le type de curseur en survolant ces briques.
.scrollUpButton {
cursor: pointer;
}
.scrollBottomButton {
cursor: pointer;
}
Le script ci-dessous, permet de définir les propriétés du composant scroll, et de brancher les événements click, drag-and-drop et mouse-wheel sur les différentes briques du composant scroll.
En premier lieu, le script doit définir les dimensions des différentes briques du composant scroll ainsi que leur positionnement. Pour cela, nous avons utilisé la fonction css de jQuery pour accéder et modifier les propriétés des différents styles.
Nous commençons par initialiser la taille de la barre de scroll et la sensibilité de déplacement dans l’espace de scrolling. Ces propriétés sont définies suivant le nombre total de pages et le nombre maximum de lignes à afficher par page.
Si le nombre de pages est inférieur à 6 :
// Hauteur dynamique de la barre de scroll selon le nombre de pages (1) scrollBarHeight = ((maxRowsPerPage-2) * rowHeight) / totalPages; // 2 correspond aux boutons de navigation qui sont font partie de l'espace de données. // Sensibilité de déplacement de la barre de scroll (2) sensibility = ((maxRowsPerPage-2) * rowHeight) / totalPages;
Si le nombre de pages est supérieur ou égal à 6 :
// Hauteur fixe du composant scrollBar (1)
scrollBarHeight = ((maxRowsPerPage - 2) * rowHeight) / 6;
var oldsensibility = ((((maxRowsPerPage-2) * rowHeight) / totalPages) - (scrollBarHeight / totalPages));
var diff = (((rowHeight * maxRowsPerPage) - (rowHeight+scrollBarHeight)) -
(rowHeight + (oldsensibility * (totalPages - 1))) ) / (totalPages - 1);
// Sensibilité de déplacement du composant scrollbar (2)
sensibility = oldsensibility + diff;
Le positionnement de la barre de scroll, et les dimensions des autres briques du composant scroll sont définies par la suite.
// Positionnement du scrollbar selon la page courante (3)
scrollBarBottom = parseFloat(scrollUpButton.position().top)+rowHeight;
var initialScrollBarTop = scrollBarBottom + ((currentPage * sensibility) - sensibility);
scrollBar.css('top',initialScrollBarTop+'px');
// Définition de la taille du composant scroll (4)
scrollComponent.css('height', maxRowsPerPage * rowHeight+'px');
// Définition de la taille d'espace de scrolling (5)
scrollBarContainer.css('height', (maxRowsPerPage - 2) * rowHeight+'px');
// Définition de la taille des boutons de navigation (6)
scrollUpButton.css('height',rowHeight+'px');
scrollBottomButton.css('height',rowHeight+'px');
// Définition de la taille de la barre de scroll
scrollBar.css('height',scrollBarHeight+'px');
// Numéro de page courante (7)
pageNumberLabel.html(currentPage + '/' + totalPages);
Nous utilisons l’évènement click de l’API jQuery pour utiliser les boutons de navigation.
// Reculer d'une page
scrollUpButton.click(function() {
previousPage();
});
// Avancer d'une page
scrollBottomButton.click(function() {
nextPage();
});
// Avancer d'une page
function nextPage() {
if (currentPage == totalPages) return false;
currentPage = currentPage + 1;
changePage();
}
// Reculer d'une page
function previousPage() {
if (currentPage == 1) return false;
currentPage = currentPage - 1;
changePage();
}
// Changer les propriétés de la barre de scroll
function changePage() {
// Nouvelle position selon la page en cours
var newTop = scrollBarBottom + ((sensibility * currentPage) - (sensibility));
scrollBar.css('top',newTop+'px');
// Mise à jour du numéro de la page
pageNumberLabel.html(currentPage + '/' + totalPages);
}
Pour donner la vie à notre barre de scroll, nous utilisons la fonction draggable de l’API jQuery UI.
// Evènement DragAndDrop sur le scrollbar
scrollBar.draggable({ axis:'y', opacity: 0.70, currentPage: 'pointer', containment: "#scrollBarContainer"+componentId,
stop: function(event, ui) {
// Changer les propriétés de la barre de scroll
changePage();
// Charger la page en cours
loadPage();
},
drag: function(event, ui) {
// Définir la page en cours selon la position de la barre de scroll dans l'espace de scrolling
var p = scrollBar.position();
var scrollBarPosition = parseFloat(p.top+sensibility);
currentPage = Math.round((scrollBarPosition - scrollBarTop) / sensibility);
pageNumberLabel.html(currentPage + '/'+ totalPages);
}
});
Pour gérer la souris molette ou le mouse-wheel, nous utilisons le plugin mousewheel de l’API jQuery. Pour des performances optimales, nous attendons un laps de temps après l’arrêt pour charger la dernière page sélectionnée. Toutefois, la barre de scroll change de position pour une meilleure interaction avec l’utilisateur.
// Evènement molette souris sur le composant scroll
scrollComponent.bind('mousewheel', function(event, delta) {
var direction = delta > 0 ? 'Up' : 'Down';
if (direction =='Up') {
previousPage();
}
else if (direction =='Down') {
nextPage();
}
clearTimeout(jQuery.data(this, 'timer'));
jQuery.data(this, 'timer', setTimeout(function() {
loadPage();
}, 250));
});
// Charger la page en cours
function loadPage() {
if (alreadyLoadedPage != currentPage) {
alreadyLoadedPage = currentPage;
// Appel asynchrone pour charger les données ...
}
}
La fonction Ready de jQuery est utilisé pour activer le composant et définir ses propriétés une fois le document DOM est entièrement chargé.
jQuery(document).ready(initialiseScroll);
function initialiseScroll() {
var scrollComponent = scrollComponentFunction({
componentId: 'test1',
currentPage : 1,
totalPages: 5,
maxRows: 10
});
}
Le projet de création de notre Framework UI vient en remplacement du Framework RichFaces qui constituait un frein à l’évolution de notre application à tous les niveaux : (développements, intégration avec d’autres technologies, tests IHM, performances des pages générées, évolutivité des composants de base …). En combinant quelques technologies et Frameworks de base (HTML, CSS, jQuery, jQuery UI, Java, …), nous avons réussi à créer des composants UI légers, performants et facilement intégrables … Utiliser des technologies de base pour développer notre propre Framework s’est avéré un pari gagnant. Toutefois, créer son propre Framework est un travail laborieux et couteux dans le temps mais les résultats sont plus que satisfaisants. Utiliser des technologies de base ou partir sur des Frameworks comme GWT, RichFaces … ? C’est un sujet auquel nous essayerons de répondre dans d’autres articles.
Ressources :
Article printed from Blog Xebia France: http://blog.xebia.fr
URL to article: http://blog.xebia.fr/2011/11/30/composant-scroll-avec-jquery/
Click here to print.