Ce blog a déménagé et parle maintenant uniquement anglais.

This blog has moved and now only speaks English.

blog.floriancargoet.com

See you there!

/home/florian

le blog de florian cargoet : du linux, du web et du logiciel libre



Une liste alimentée automatiquement avec jQuery

5 March, 2009 (19:58) | jQuery, Webdev | Florian Cargoet

Catégorie Webdev : A propos du web, de son contenu, de ses outils...

Dans ce petit tutoriel sur jQuery, nous allons créer une liste dont les éléments seront mis à jour automatiquement. Ces éléments seront récupérés en AJAX1 régulièrement et insérés avec une animation. On utilisera jQuery pour la partie cliente et PHP/MySQL pour la partie serveur. Vous pouvez tester tout de suite le résultat.

Coté serveur

Pour les données, voici une petite table SQL en UTF8 :

?Download data.sql
CREATE TABLE IF NOT EXISTS `liste_dynamique` (
  `ID` int(11) NOT NULL auto_increment,
  `ITEM` text collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;
 
INSERT INTO `liste_dynamique` (`ID`, `ITEM`) VALUES
(1, 'Je suis un li ajouté dynamiquement !'),
(2, 'Moi aussi je viens de la base de données');

En ce qui concerne le PHP, cela va différer selon que vous soyez en PHP5 ou encore en PHP4 (dans ce cas, il est temps de changer). En effet, en PHP4, le JSON n’est pas supporté nativement, il vous faudra inclure un fichier externe qui vous donnera le support JSON. Voici le code en PHP5 :

?Download items.php
<?php
    //le client va spécifier à chaque requête quels items il veut qu'on lui renvoie.
    //Pour ça, on utilise la variable "limit", passée en GET
    $user_limit = $_GET['limit'];
    //il faudrait sécuriser la variable utilisateur !
    //par exemple, vérifier que c'est un nombre entier
 
    //connexion à la bdd
    mysql_connect('host','login','password');
    mysql_selectdb('database');
    mysql_query("SET NAMES 'utf8'");
 
    //on ne récupère que ce qui est nécessaire
    $sql_result = mysql_query('SELECT * FROM liste_dynamique WHERE ID > '.$user_limit);
 
    //on crée un array qui sera converti en JSON à la fin
    $items = array();
    //lastId sert à trouver l'id le plus grand pour le retourner au client
    $lastId = $user_limit;
 
    while($row = mysql_fetch_assoc($sql_result))
    {
        $items[] = $row['ITEM'];
        $lastId = $lastId > $row['ID'] ? $lastId : $row['ID'];
    }
 
    //on envoie les données en JSON
    header('Content-type:application/json');
    echo json_encode(array(
        'lastId' => $lastId,
        'items' => $items
    ));
 
?>

Voilà pour ce qui se passe sur le serveur. En résumé :

  • Les items de la liste sont stockés en base de données.
  • Le code PHP traite les requêtes du client en lui renvoyant uniquement les éléments qu’il n’a pas déjà, le tout au format JSON.

Du coté du client…

Avant d’attaquer le code jQuery, mettons en place un code HTML minimal avec la liste qui sera dynamiquement remplie.

?Download liste.html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>jQuery Demo : Liste</title>
    <script type="text/javascript" src="js/jquery.js"></script>
    <script type="text/javascript" src="js/liste.js"></script>
    <link rel='stylesheet' href='liste.css' type='text/css' />
</head>
<body>
    <ul>
        <li>Element ajouté statiquement</li>
    </ul>
</body>
</html>

Attaquons enfin le code JavaScript !

?Download liste.js
$(function(){
 
    var list = $('ul'); //on récupère la liste
    var lastId = 0;  //d'abord, on veut récupérer tous les éléments existants
 
    //la fonction addToList traite la réponse du serveur, en extrait les éléments
    //et les ajoute à la liste en les masquant puis les affiche avec une animation
    var addToList = function(data)
    {
        lastId = data.lastId; //on retient l'id du dernier item pour le prochain appel au serveur
        $.each(data.items, function(index, item){ //on boucle sur l'array data.items
            $('<li>'+item+'</li>').hide().prependTo(list).slideDown('slow');
        });
    }
 
    //getNewListItem fait l'appel AJAJ au script PHP en lui passant le lastId en paramètre.
    //la callback sera appelée lorsque le serveur aura répondu
    var getNewListItem = function(){
        $.getJSON('items.php', { limit : lastId }, function(data){
            //on traite le résultat
            addToList(data);
            //on programme la requête suivante
            setTimeout(getNewListItem, 3000);
        });
    }
    //la première requête démarre dans 3 secondes
    setTimeout(getNewListItem, 3000);
 
});

Et enfin un peu de CSS parce que c’est un peu moche pour l’instant :

?Download liste.css
li
{
    list-style:none;
    margin:5px;
    padding:5px;
    background-color:green;
    width:500px;
    color:white;
    text-align:center;
}

Bon, d’accord, c’est encore plus moche…

Et voilà, nous avons maintenant une liste avec un seul élément qui se remplira toutes les 3 secondes si des éléments ont été ajoutés dans la base de données. Tout cela est bien sûr améliorable (sécurisation en PHP, meilleur style CSS, animation plus riche…) mais je vous le laisse en exercice !

  1. ou plutôt AJAJ, Asynchronous Javascript And JSON []

Commentaires

Commentaire de 20syl
le 16 June 2009, 14:14

Merci pour ce tutoriel.
Concernant la sécurisation en PHP, quelles protections conseilles-tu ?
Et sinon, je souhaiterais n’afficher qu’une liste de 10 elements, comment puis-je procéder ?

Commentaire de Florian Cargoet
le 16 June 2009, 16:02

Toute donnée saisie par l’utilisateur doit être controlée côté serveur, sinon il y a des risques. Par exemple, dans cet exemple, la variable $limit est collée directement dans la requête SQL ce qui permet à n’importe qui d’insérer du code SQL et de récupérer des données de la base.
De manière générale, renseigne toi sur les injections SQL (il y a des fonctions du genre mysql_real_escape_string(); ). Ici, je sais que je veux un nombre donc je vérifie simplement que ça en est un (expression régulière, is_int(), ctype_digit()…).

Enfin, pour limiter à 10 éléments, il faut supprimer les anciens éléments au fur et à mesure donc il faut :

  • vérifier si le serveur a retourné plus de 10 éléments, si oui, tronquer.
  • compter combien d’éléments il reste à insérer
  • supprimer la différence entre 10 et ce nombre avec quelque chose du style :
    ?View Code JAVASCRIPT
    $('li:gt(3)').remove() // supprime les 6 plus vieux éléments

Bonne chance !

Commentaire de bliblia
le 29 August 2010, 22:29

salut
c’est sympa mais, c’est un vieux code ? car il ne fonctionne plus avec la nouvelle version de jquery 1.4.2
u saurais comment retablir ton script ?
merci pour ta reponse,

Commentaire de Florian Cargoet
le 30 August 2010, 14:43

Le post date de mars 2009 donc oui, c’est un peu vieux. C’était avec jQuery 1.3.2. Je n’ai pas beaucoup touché à jQuery depuis donc je n’ai pas de solution immédiate.
Vu le peu de jQuery qui est utilisé, ça doit pas être compliqué à rectifier, si ?

Commentaire de bliblia
le 30 August 2010, 21:36

merci pour ta reponse rapide !
en faite, je pige assez mal le jquery et faire une mise a niveau de ton code je comprends pas !
et en plus de ca, j’ai copier la version de jquery que tu utilise pour ton code, et il me pourri le design…
enfin je vaisessayer de trouver un equivalent parce que j’adore ton code mais si tu n’as pas de solution.. ^^ je vais me manger les doigts !

merci !

Commentaire de Florian Cargoet
le 30 August 2010, 23:34

Ok je comprends. Si tu trouves ce morceau de code complexe, je te conseille d’explorer un peu la doc officielle (pas mal fichue) pour comprendre un peu comment ça fonctionne.
A part la partie getJSON, je viens de faire le test avec jQuery 1.4.2 et ça fonctionne. Donc le problème vient soit d’un changement dans getJSON soit dans un problème sur les données renvoyées par ton serveur. Tu es sûr de ton code coté serveur ?

Commentaire de bliblia
le 26 November 2010, 20:08

salut en effet en faite le getjson fonctionne mais c’est le tableau qui s’affiche pas !
et lastid est tout le temps a 0; meme si je fais des ajouts dans la base de données

le json me renvoi ca
lastId : 0,
items : ["Je suis un li ajout\u00e9 dynamiquement !","Moi aussi je viens de la base de donn\u00e9es","5465465465464","65498409\r\n","045646+408907\r\n","dsfdfdsfsdfsdfs"]

Commentaire de Florian Cargoet
le 26 November 2010, 21:12

Ok, je me suis penché sur le problème et je l’ai trouvé. C’est la callback qui n’était pas appelée après la réponse du serveur. Du coup, la liste n’était pas remplie et le lastId n’était pas mis à jour.

Maintenant, pourquoi la callback n’était pas appelée :

D’après la doc : As of jQuery 1.4, if the JSON file contains a syntax error, the request will usually fail silently. Avoid frequent hand-editing of JSON data for this reason. JSON is a data-interchange format with syntax rules that are stricter than those of JavaScript’s object literal notation. For example, all strings represented in JSON, whether they are properties or values, must be enclosed in double-quotes.

Résumé en français : depuis jQuery 1.4, la requête plante sans rien dire si le JSON n’est pas syntaxiquement irréprochable. Par exemple (et c’était le cas ici) les clés de l’objet retourné doivent être entre guillemets doubles.

Donc, solution rapide : rajouter des guillemets autour des clés.
Solution intelligente (j’ai mis à jour l’article en ce sens) : utiliser json_encode pour faire du JSON correctement.

Commentaire de Florian Cargoet
le 26 November 2010, 21:24

Suite au problème que tu as relevé, j’en ai trouvé un autre, coté JavaScript cette fois ci :
si une requête A est trop lente puis qu’une requête B est lancée avant la réponse de A, B sera lancée avec les mêmes paramètres que A. Le lastId pourra donc ne pas être à jour et on se retrouvera avec des doublons dans la liste…
J’ai corrigé ça en utilisant setTimeout à chaque réponse plutôt que setInterval une seule fois. On ne programme une nouvelle requête que si la précédente est terminée.

Commentaire de bliblia
le 26 November 2010, 22:22

aaaah ! je savais bien qu’il y avait un probleme :)
tout fonctionne nickel !
ton code est super interessant et j’avais beau navigué de sites en sites. j’avais trouvé un truc pareil !
une chose si ! publie plus de bonnes choses comme ca !

merci

Commentaire de bliblia
le 26 November 2010, 22:41

euh oui, par contre ! sur firebug il semble que le nouveau setTimeout pose probleme
car sur firecbug, ca rafraichi par boucle un peu bizzarre..
on dirait que ca me boucle l’appelle json autant de fois que lastid (si lastid est a 21 ca me fait 21 appels json d’un coup) je sais pas si tu vois ce que je veux dire ?

sur ta demo ca ne fait pas ca.

Commentaire de bliblia
le 26 November 2010, 22:44

mdr ok c’est moi le boulet !
j’avais laisser l’ancien setInterval() ! ca fait un truc tres bizarre si tu le laisse !! ^^
donc maintenant ca marche a pleine vitesse !
cimouss !

Commentaire de bliblia
le 20 December 2010, 7:12

yo rebonjour !
j’aurais besoin d’aide sur un bout de code avec jquery.
est ce que je peux te l’envoyer ?

bliblia;)

Commentaire de Florian Cargoet
le 21 December 2010, 12:13

Je veux bien jeter un coup d’œil quand j’aurais le temps mais je précise que je ne tiens vraiment pas à devenir debugger de code pour chaque visiteur ;-) Merci de ne pas en faire une habitude.