Partage

[jQuery] Exercice (n° 3) pour s'entraîner

Notes de bas de page

Sujet résolu
12 janvier 2012 à 13:56:52

Bonjour à tous,

Je vous propose aujourd'hui un troisième exercice (voir le n° 1 et le n° 2) sur jQuery, dans l'idée de faire progresser les membres mais aussi d'animer la section. ;)

Avant d'entrer dans le vif du sujet, il y aura un certain nombre de règles à respecter pour que tout se passe bien dans ce topic.

Règles


  1. Le troll et le flood ne sont pas tolérés. Inutile de balancer des "Pourquoi jQuery et pas Mootools ?", les messages non constructifs seront modérés.
  2. Je vous fournis le code HTML, vous n'aurez pas le droit de le modifier.
  3. Votre réponse contiendra uniquement du code jQuery avec la balise <secret> de manière à respecter ceux qui n'ont pas commencé l'exercice.
  4. Evidemment, le code que vous posterez devra être de vous, pas la peine de recopier un code externe, cela n'a pas d'intérêt. L'utilisation de plugins n'est pas autorisée.


Objectif du script


Description


Nous allons créer dynamiquement des notes de bas de page sur certains mots dans un texte. Ces mots seront définis, le HTML également (interdit de le modifier). Une portion de CSS sera également fournie.
Au clic sur un mot, cela devra mener vers l'élément qui contient les notes (comprendre une ancre, la même pour tous).

HTML



<div id="texte">
	<p>PARIS (AFP) - Les prix à la consommation en France ont encore grimpé de 0,4% en décembre par rapport au mois précédent, pour atteindre 2,5% sur un an (+2,4% hors tabac), a annoncé jeudi l'<a href="#" class="footnote" data-note="l'Institut national de la statistique et des études économiques">Insee</a>.</p>

	<p>Théoriquement, ce taux d'<a href="#" class="footnote" data-note="L'inflation est une baisse durable de la valeur de la monnaie">inflation</a> devrait faire monter le taux du Livret A de 2,25% à 2,50%, voire 2,75% au 1er février.</p>

	<p>Mais le gouverneur de la Banque de France (BdF) Christian Noyer a déjà préparé les esprits à son éventuel maintien en l'état, expliquant que l'inflation devrait baisser dans les prochains mois et qu'il serait donc inutile de relever le rendement du <a href="#" class="footnote" data-note="Le livret A est le compte d'épargne réglementé et défiscalisé français le plus utilisé">Livret A</a> pour le diminuer dans la foulée.</p>
</div>



CSS



a.footnote, a.footnote:visited {
	color:black;
	text-decoration:none;
	border-bottom:1px dashed blue;
}
a.footnote:hover {
	cursor:pointer;
}
#notes {
	width:100%;
	background-color:#EAEAEA;
}



Rendu FINAL


Image utilisateur


Remarques


#notes correspond au bloc (avec le fond gris) qui contient toutes les notes, cet élément n'est pas présent par défaut.
N'oubliez pas les exposants pour chacun des mots (3 dans cet exercice). Ils devront bien sûr être dynamiques.

Ressources




Bon code ! :pirate:

Il n'y a pas une seule solution, je donnerai plus tard ma version du script commentée, j'attends un peu pour ne pas vous inciter à la regarder. :)
12 janvier 2012 à 14:48:33

Voici ce que j'ai fait :

var $nb = 0;

$("#texte").append('<br /><br /><div id="notes" ><ol id="lst" ></ol></div>');

$('.footnote').each(function(){
    $nb++;
    $(this).append(' <sup>'+$nb+'</sup> ');
    $(this).attr('href', '#n_'+$nb);
     $("#lst").append('<li id="n_'+$nb+'" >'+$(this).attr('data-note')+'</li>');
                    
});


En ligne ici :

http://jsfiddle.net/dom0796/Z4WTk/64/embedded/result/

Il y a un seul problème, c'est que ma liste <ol> ne marche pas, pourtant, le code produit est bon !
Voici le code que me donne Chrome :

<ol id="lst"><li id="n_1">l'Institut national de la statistique et des études économiques</li><li id="n_2">L'inflation est une baisse durable de la valeur de la monnaie</li><li id="n_3">Le livret A est le compte d'épargne réglementé et défiscalisé français le plus utilisé</li></ol>


D'ailleurs, quand on teste ça ici http://www.w3schools.com/html5/tryit.a [...] yhtml5_button , ça marche.

J'en déduis que c'est sans doute JsFiddle qui doit faire ça ...
Enfin, c'est étonnant quand même ...
12 janvier 2012 à 14:52:57

Merci d'avoir essayé.

Ta ligne 3 n'est pas propre mais fonctionnelle. :)

Pour le <ol> c'est jsFiddle qui fait ça (j'ai la même chose) donc pas de problème.

Dans mon rendu final l'exposant n'est pas souligné. :)

$(this).attr('data-note') doit être remplacé par $(this).data('note')
12 janvier 2012 à 14:59:57

Ma petite contribution ;)


var dataNotes = '';

$('.footnote').each(function(i) { 
  var noteIndex = 'note-'+(i+1);
  
   dataNotes += '<li id="'+noteIndex+'">'+ $(this).data('note')+ '</li>';
   $(this).attr('href', '#'+noteIndex).append($('<sup />').text(i+1))
});   

$('#texte').after($('<ol />', {
  'id':'notes'
}).append(dataNotes))

12 janvier 2012 à 15:07:20

ah oui c'est vrai, voilà qui est mieux :

var $nb = 0;
var $text = $('#texte');

$text.after('<br /><br /><ol id="notes" ></ol></div>');

var $notes = $('#notes');

$('.footnote').each(function(){
    $nb++;
    $(this).after(' <sup>'+$nb+'</sup> ');
    $(this).attr('href', '#n_'+$nb);
    
    $notes.append('<li id="n_'+$nb+'" >'+$(this).data('note')+'</li>');
                    
});


Pour les exposant à l'extérieur du lien, j'avais vu, mais je ne connaissais pas la fonction after().
Un petit tour à la doc m'a renseigné ;)

EDIT

au passage, l'idée était bonne, je n'y aurait pas songé !
12 janvier 2012 à 15:23:46

Allez voila ma contribution:


$(document).ready(function() {
        
        if($('.footnote').length > 0){
            var str="<div id='notes'><a name='footn'></a>";
            $('a.footnote').each(function(i){
                str += i+". "+$(this).data('note')+"<br />";
            });
            $('body').append(str+"</div>");
            
            $('#texte').on('click','.footnote',function(){
                document.location.hash = '#footn';
                return false;
            });
        }
    });



Points particuliers:

  • J'ai encapsulé le tout dans un if pour eviter d'avoir a executer du code inutilement si pas de footnote (superflu pour l'exercice j'en conviens)
  • Je n'ai pas utilisé de <ol></ol> parce qu'il semble que le temps d’exécution est plus important que lorsque l'on utilise directement l'index du each (le résultat reste identique pour l'utilisation actuelle)
  • Il était possible d'utiliser un scrollTo pour ne pas avoir a afficher le hash dans l'url, cela dit, le temps d’exécution prend une à deux millisecondes dans la face, donc vu qu'il était mentionné "ancre" dans l’énoncé je suis parti du principe que le hash ne posait pas de problème

12 janvier 2012 à 15:29:49

Merci pour la contribution loun4st4ck.

Merci aussi Str!k3z,
Le test du .length est judicieux ! :)
En revanche le <ol> ici est justifié. Ta ligne 10 par contre est inutile, les éléments sont déjà présents dans le DOM donc tu n'as pas besoin de faire comme cela. Et ça te fait un gestionnaire d'événement inutile d'ailleurs.
12 janvier 2012 à 15:32:47

Effectivement pour <ol> si tu parle de l'espacement a gauche c'etait la seule solution sans retoucher au css, j'avais pas fait gaffe. Pour ce qui est de la ligne 10 tu veux bien dire que remplacer le .on par un .click etait suffisant ou j'ai mal compris ?
12 janvier 2012 à 15:35:34

Citation : Str!k3z

Effectivement pour <ol> si tu parle de l'espacement a gauche c'etait la seule solution sans retoucher au css, j'avais pas fait gaffe. Pour ce qui est de la ligne 10 tu veux bien dire que remplacer le .on par un .click etait suffisant ou j'ai mal compris ?

Tu as fait un .on() façon .live() (c'est-à-dire avec 3 arguments) qui est seulement nécessaire si c'est pour écouter un événement créé par jQuery.
12 janvier 2012 à 15:37:30

@Desolation: ce serait bien de mettre en place une test jsperf ;)
12 janvier 2012 à 15:40:48

Citation : loun4st4ck

@Desolation: ce serait bien de mettre en place une test jsperf ;)



un test automatique ?
oui, bonne idée, mais assez hard comme projet (je parle d'un analysateur automatique).
12 janvier 2012 à 15:41:13

MDR le topic secret
Damned j'aurais mal lu ton article sur le .on alors...
12 janvier 2012 à 15:44:23

Citation : loun4st4ck

@Desolation: ce serait bien de mettre en place une test jsperf ;)


Le but du topic est de voir les différentes méthodes/logique en demandant à plusieurs personnes de coder sur un même énoncé, tout le monde fait différemment.

Du côté performance je ne trouve pas intéressant de débattre sur des millisecondes, surtout que ce qui changera, c'est la vitesse de chargement de jQuery qui est différente selon le débit, s'il est en cache ou pas... Bref nous aurons tous des résultats différents car plusieurs paramètres entrent en compte.

Mais essayons de ne pas nous écarter du sujet initial. :)
13 janvier 2012 à 8:56:46

Point important :
$() est une fonction lourde dont il faut absolument éviter d'abuser !

Mettez vos sélections dans des variables ! >_<

@Dominique0796 :

var $nb = 0;
var $text = $('#texte');

$text.after('<br /><br /><ol id="notes" ></ol></div>');

var $notes = $('#notes');

$('.footnote').each(function(){
    $nb++;
	var $this = $(this);
    $this.after(' <sup>'+$nb+'</sup> ');
    $this.attr('href', '#n_'+$nb);
    
    $notes.append('<li id="n_'+$nb+'" >'+$this.data('note')+'</li>');
    
});



@loun4st4ck :

var dataNotes = '';

$('.footnote').each(function(i) { 
	var noteIndex = 'note-'+(i+1);
	var $this = $(this);
	dataNotes += '<li id="'+noteIndex+'">'+ $this.data('note')+ '</li>';
	$this.attr('href', '#'+noteIndex).append($('<sup />').text(i+1))
});   

$('#texte').after($('<ol />', {
	'id':'notes'
}).append(dataNotes));


@Str!k3z :
$(document).ready(function() {
	
    var $footnotes = $('a.footnote');
	// ".footnote" ou "a.footnote", il faut choisir...
	// Sinon ton code a un côté illogique.
	
	if($footnotes.length > 0){
		var str="<div id='notes'><a name='footn'></a>";
		$footnotes.each(function(i){
			str += i+". "+$(this).data('note')+"<br />";
		});
		$('body').append(str+"</div>");
		
		$('#texte').on('click','.footnote',function(){
			document.location.hash = '#footn';
			return false;
		});
	}
});


Personnellement, l'utilisation faite du .on() par Str!k3z ne me traumatise pas.
Ca a le mérite de ne placer qu'un seul écouteur au lieu d'en placer autant qu'il y a de notes.
Mais ça n'apporte pas grand chose niveau optim', vu que de toutes façons, on a déjà parcouru chaque lien.

Ce qui me choque plus, c'est l'utilisation de return false et la modification du location, contrairement aux autres qui ont modifié le href des liens (ce qui est plus sémantique)...
A défaut, utiliser e.preventDefault() serait réellement préférable à return false pour laisser l'événement se propager tranquillement.
13 janvier 2012 à 9:23:30

Merci Golmote pour le rappel du $ même si je le répète à chaque fois. ^^

Voici ma version :
var $this = $('#texte');
var $footnotes = $('a.footnote', $this);

if ($footnotes.length > 0) {
	var notes = 'notes';

	// creating the notes div
	$('<div>', {
		'id': notes,
		'class': 'footnotes'
	}).appendTo($this);

	// creating le list
	$('<ol>').appendTo('#' + notes);

	// for each note
	$('a.footnote', $this).each(function(i) {
		i++;
		$(this).attr('href', '#' + notes).after(' <sup>' + i + '</sup>');

		// inserting the note into the list
		$('<li>', {
			html: $(this).data('note')
		}).appendTo('#' + notes + ' ol');
	});
}


Je me suis inspiré de cet exercice pour en faire un plugin qui fait la même chose à peu de chose près qu'il peut y avoir plusieurs textes sur la même page.
Voir la news sur mon blog.
13 janvier 2012 à 13:21:50

Citation : Golmote

Point important :
$() est une fonction lourde dont il faut absolument éviter d'abuser !

Mettez vos sélections dans des variables ! >_<

@Str!k3z :

$(document).ready(function() {
	
    var $footnotes = $('a.footnote');
	// ".footnote" ou "a.footnote", il faut choisir...
	// Sinon ton code a un côté illogique.
	
	if($footnotes.length > 0){
		var str="<div id='notes'><a name='footn'></a>";
		$footnotes.each(function(i){
			str += i+". "+$(this).data('note')+"<br />";
		});
		$('body').append(str+"</div>");
		
		$('#texte').on('click','.footnote',function(){
			document.location.hash = '#footn';
			return false;
		});
	}
});


Personnellement, l'utilisation faite du .on() par Str!k3z ne me traumatise pas.
Ca a le mérite de ne placer qu'un seul écouteur au lieu d'en placer autant qu'il y a de notes.
Mais ça n'apporte pas grand chose niveau optim', vu que de toutes façons, on a déjà parcouru chaque lien.

Ce qui me choque plus, c'est l'utilisation de return false et la modification du location, contrairement aux autres qui ont modifié le href des liens (ce qui est plus sémantique)...
A défaut, utiliser e.preventDefault() serait réellement préférable à return false pour laisser l'événement se propager tranquillement.


Merci pour ces précisions, je ne savais pas vraiment pour la fonction $(), mais j'ai bien compris la problématique et j'en prend bonne note.
Pour le return false, j'ai oublié de le virer je m'en suis même pas rendu compte, je n'aurais pas du le mettre, il est totalement inutile a mon sens (en revanche si tu peux revenir sur l’intérêt du preventDefault() dans ce cas de figure je suis pas contre parce que je ne suis pas sur d'avoir saisi toute la subtilité de ce que tu as ecrit)
13 janvier 2012 à 15:15:03

C'est vrai, j'ai pas pensé à mettre mes sélecteurs en cache ;)
Il y a un détail qui m'embête (avis personnel) chez beaucoup de dev JS: lancer une manipulation directe du DOM à l'intérieur d'une boucle alors qu'on peut faire autrement(*). Il faut savoir que les manipulations directes du DOM sont souvent à l'origine de multiples reflow...


(*) Par exemple, stocker les items dans un string et faire un seul append ;)


13 janvier 2012 à 19:05:28

Tu dis vrai loun4st4ck, et en ça j'ai bien aimé ta concaténation :)

@Str!k3z : L'histoire du return false, en quelques lignes :

En JS, lorsqu'un événement se produit, il se propage.
Typiquement, quand tu cliques sur un élément, l'événement parcourt tout l'arbre DOM pour arriver jusqu'à l'élément en question, et déclenche l'écouteur d'événement approprié, s'il existe. Ensuite, l'événement remonte l'arbre DOM et déclenche ainsi les gestionnaires d'événements placés sur les éléments parents.
On peut imaginer de nombreux cas où ce comportement est utile.
Ce comportement est appelé la propagation de l'événement.

Lorsqu'on fait un return false dans un écouteur d'événement avec jQuery, la conséquence principale est d'empêcher le navigateur d'effectuer l'action par défaut associée (suivre un lien au clic, par exemple). Mais l'autre conséquence est de stopper la propagation de l'événement, qui ne remontera donc pas, empêchant les écouteurs d'événement des éléments parents d'être déclenchés.

Les fonctions utilisées comme écouteurs d'événements reçoivent un paramètre, qu'on appelle souvent e (comme événement). Ce paramètre a notamment deux méthodes : preventDefault() qui sert à empêcher l'action par défaut du navigateur, et stopPropagation() qui stoppe la propagation de l'événement.

Bref. Tout ça pour dire que si votre intention est d'empêcher le comportement par défaut, alors utilisez preventDefault() et non pas return false. Car peut-être voulez-vous conserver la propagation !

Des exemples pour conclure :

<script type="text/javascript">
jQuery(function($) {
	
	// return false
	$('#test1').click(function() {
		alert('Test 1 : div');
	}).find('a').click(function() {
		alert('Test 1 : a');
		return false;
	});
	
	// preventDefault uniquement
	$('#test2').click(function() {
		alert('Test 2 : div');
	}).find('a').click(function(e) {
		e.preventDefault();
		alert('Test 2 : a');
	});
	
	// preventDefault et stopPropagation
	// ( + ou - équivalent à return false, donc.)
	$('#test3').click(function() {
		alert('Test 3 : div');
	}).find('a').click(function(e) {
		e.preventDefault();
		e.stopPropagation();
		alert('Test 3 : a');
	});

});
</script>

<div id="test1">Texte du div <a href="http://www.google.fr">Lien</a></div>
<div id="test2">Texte du div <a href="http://www.google.fr">Lien</a></div>
<div id="test3">Texte du div <a href="http://www.google.fr">Lien</a></div>


En conclusion, on peut considérer que return false est rarement utile. preventDefault() et stopPropagation() offrent un meilleur contrôle de l'événement (et bonus : ils peuvent se placer au début de la fonction et donc agir même si la suite du code plante... :-° )
13 janvier 2012 à 19:19:35

@Golmote:
Merci beaucoup de toutes ces précisions.
Je connaissais preventDefault et stopPropagation (du moins je les avait déjà utilisé) mais je n'avais une vue aussi précise de leurs répercussions ni des comparaisons avec l'action du return false.
Pour l'utilisation que j'en fait, je bannis donc le return false (dans la mesure du possible) et me dirige de ce pas vers du code plus propre.

Tant que j'y suis, il m'arrive de rencontrer dans des bouts de code a droite a gauche des onclick="javascript:void(0)", je n'en saisi pas l'utilité et d'instinct ça me parait sale. Si tu as 3 minutes de plus pour eclairer ma lanterne, j'en serais ravi.
13 janvier 2012 à 20:22:14

merci beaucoup, tu m'en apprend pas mal là :D
C'est de ce genre d'explications qui sont précieuses (et que l'on ne trouve pas dans les tutos).
13 janvier 2012 à 21:35:45

Citation : Str!k3z


Tant que j'y suis, il m'arrive de rencontrer dans des bouts de code a droite a gauche des onclick="javascript:void(0)", je n'en saisi pas l'utilité et d'instinct ça me parait sale. Si tu as 3 minutes de plus pour eclairer ma lanterne, j'en serais ravi.



Je ne suis pas golmote, mais je veux déjà te dire que javascript: est inutile, on ne peut que mettre du javascript dans un onclick (corrigez moi si je me trompe)
Pour void, si j'ai bien compris c'est un opérateur qui renvoi undefined, mais golmote t'expliquera mieux son utilisation que moi ...

En résumé, le code onclick='javascript:void(0)' est inutile (encore une fois corrigez moi si je me trompe) car par défaut, au clic, rien ne se passe ...
Je vous ai aidé ? Appuyez sur le bouton "Ce message est utile", avec le pouce levé vers le haut !  (en bas à gauche de mon message)
13 janvier 2012 à 21:37:37

Je ne suis pas Golmote non plus mais à mon avis ça fait le même comportement qu'un return false;
13 janvier 2012 à 22:29:44

crf dit vrai concernant "javascript:". Ce pseudo-protocole n'aurait d'"intérêt" que dans un href (mais c'est bien évidemment à bannir quand même, vous le savez surement...)

Mettre un void(0) dans un onclick, vraiment, je n'en vois pas l'utilité. Desolation, après test, il s'avère que ça ne bloque même pas le comportement par défaut.

A mon avis, c'est plutôt href="javascript:void(0)" que tu as vu. Et ça, c'est une façon très sale de faire des liens qui n'en sont pas, et qui ne remontent pas en haut de page si on clique dessus (contrairement au href="#" qui oblige à stopper le comportement par défaut du navigateur).

Bref, c'est caca, faut pas utiliser, toussa toussa ; vous connaissez la chanson.
14 janvier 2012 à 11:10:52

Citation : Golmote

crf dit vrai concernant "javascript:". Ce pseudo-protocole n'aurait d'"intérêt" que dans un href (mais c'est bien évidemment à bannir quand même, vous le savez surement...)

Mettre un void(0) dans un onclick, vraiment, je n'en vois pas l'utilité. Desolation, après test, il s'avère que ça ne bloque même pas le comportement par défaut.

A mon avis, c'est plutôt href="javascript:void(0)" que tu as vu. Et ça, c'est une façon très sale de faire des liens qui n'en sont pas, et qui ne remontent pas en haut de page si on clique dessus (contrairement au href="#" qui oblige à stopper le comportement par défaut du navigateur).

Bref, c'est caca, faut pas utiliser, toussa toussa ; vous connaissez la chanson.



mm d'accord.
en fait, moi j'utilisait ce href="javascript:void(0);" juste pour dire de remplir le href de mes lien, sans le #, justement pour que la page ne remonte pas en haut comme tu dis.
Et tu utilise quoi alors pour ne pas que ta page remonte ?

Voici où es-ce que j'ai vu ça :

http://opensource.steffenhollstein.de/templates/modalbox/
14 janvier 2012 à 12:38:52

bien vu Golmote, c'etait effectivement href=...
Tu arrives donc meme a corriger mes questions avant d'y repondre :)

Mon instinct ne me trompais donc pas, c'est deja ça, mais c'est toujours mieux de savoir et comprendre le pourquoi du comment.
Merci beaucoup à vous 3 de ces precisions, je suis ravi !
14 janvier 2012 à 14:13:39

Citation : Dominique0796

mm d'accord.
en fait, moi j'utilisait ce href="javascript:void(0);" juste pour dire de remplir le href de mes lien, sans le #, justement pour que la page ne remonte pas en haut comme tu dis.
Et tu utilise quoi alors pour ne pas que ta page remonte ?



Pour moi, il y a... disons 3 situations envisageables.

Tu veux faire un lien qui, quand on clique dessus, ouvre une modal box super sexy avec un formulaire de connexion. Ok, mais tu as certainement une page de connexion (en fallback pour les noJS), alors tu mets l'url de la page de connexion dans le href, et tu empêches le comportement par défaut dans le onclick.

Tu veux faire un lien qui, quand on clique dessus, scroll de manière super sexy jusqu'au footer de ton site. Là, pas de page de fallback. Mais plutôt que de mettre un bête "#" dans ton href, mets-y une ancre correspondant à ton footer. Sémantiquement, c'est mieux. Et tu empêcheras le comportement par défaut pour faire le scroll.

Enfin, tu veux faire un lien qui, quand on clique dessus fais exploser la page en un feu d'artifice super sexy, absolument pas adapté aux noJS. Mais alors pourquoi utiliser un <a> (qui est censé t'emmener quelque part) ? Sémantiquement, cela n'a aucun sens. A la place, tu peux utiliser un <button> par exemple, conçu pour cliquer dessus.
14 janvier 2012 à 21:02:10

oui c'est vrai, j'ai déjà utilisé une fois cette technique de mettre un lien fallback ...
merci encore ;)
@+
27 janvier 2012 à 19:53:09

Mon code :

$("<div id='notes'></div>").insertAfter('#texte');

$('.footnote').each(function(index) {
	$(this).append("<sup>" + index + "</sup>").attr('href', '#' + index);
	$('#notes').append("<span id='" + index + "'><sup>" + index + "</sup>:  " + $(this).attr('data-note') + "</span><br />");
});
27 janvier 2012 à 21:32:43

Mais c'est quoi cette ligne 4 ? o_O

[jQuery] Exercice (n° 3) pour s'entraîner

× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
  • Editeur
  • Markdown