31 juillet 2014

:target : pseudo-classe rafraîchie avec pushState()

En évoquant il y a trois jours ':target' et son manque d'harmonie avec une url re-formulée par "pushState()", se posait la question du problème sur IE 10 - Windows ne fait pas l'écran à la maison le week-end.
Le problème y est identique. Les solutions proposées alors fonctionnent bien sur IE 10 et 11, mais sans le déplacement dans l'historique - boutons "back" et "forward" via notamment le clavier.

Rafraîchir ':target' sans s'occuper du DOM

Il y a des solutions qui ne reposent pas sur "fixed" et qui composent un historique exploitable.

  1. La pseudo-classe ':target‘ rafraîchie après 'pushState()', navigateur standard
  2. La pseudo-classe ':target‘ rafraîchie après 'pushState()' pour IE

Elles ne sont pas communes à IE et aux autres. Par contre, elles marchent sur le même principe dans le cas d'un retour en début de page (avec clarification de l'url) ou d'une circulation globale, entre éléments de la page.

NB : les codes suivants sont sur Github.

1) En UA standard, le script de traitement du clic

ev.preventDefault();
var $t = $(this), //jQuery requis
 ancre = $t.attr("href"),
 title = document.title,
 adresse = window.location.pathname.replace(/(.*\/)+/,"");
history.pushState({ ancre : ancre },title, adresse + ancre); //(1)

 //DEBUT solution pour le rafraîchissement du :target :
history.pushState({ ancre : ancre },title,adresse); //(2)
history.go(-1);
 //FIN solution

//possibilité de scroll - $.animate({ scrollTop : n }) par exemple
/*
(1) ({ ancre : "" },title, adresse); pour un retour en début de page
(2) ({ ancre : "#" },title,adresse + "#"); pour un retour en début de page */

C'est le double pushState qui fait la solution en ouvrant à une méthode de "history" en cohérence avec ':target'.

2) Et IE

(...)
 //DEBUT solution pour le rafraîchissement du :target :
$("body").focus();
 //FIN solution
(...)

La méthode du focus est simple (et requiert l'attribut tabindex sur la balise 'body') , mais ensuite l'usage de l'historique ne passe pas plus qu'en mode normal. sans prothèse :


window.onpopstate = function(ev) {
 if (! ev.state) return;
 var ancre = ev.state.ancre;
 if (ancre) {
  history.replaceState({ ancre : ancre },
    document.title,
    window.location.pathname.replace(/(.*\/)+/,"") + ancre);
  $b.focus();
} }

Note du 04 août :l'utilisation conjointe des fonctions "back"-"forward" et de ':target' reste très aléatoire sur Internet Explorer, que le formatage par ':target' résulte d'une ancre classique dans l'url ou des fonctions de l'API "history".

Entre solutions ?

En standard et sur IE, ces deux solutions ont l'avantage de ne pas avoir à styler des éléments ni à affecter le DOM, ce que les précédentes solutions font. Par contre, sans le support de l'API "history", ces dernières restent de mise.

Elles incluent IE 8 mais posent problème pour consulter les état précédents et suivants par les boutons "back" et "forward" du navigateur. La nouvelle solution le permet - ce n'est pas du luxe au clavier - et elle exclut IE 9.

Sur les autres navigateurs, la nouvelle solution crée une entrée en +1, "forward", dans l'historique. Cette entrée fait accéder à l'url clarifiée (ou à l'url terminée par "#" après un retour en début de page) ce qui serait peut-être un inconvénient dans certain cas, mais paraît sinon inoffensif.
Pour l'éviter, il reste possible de jouer différemment avec l'historique :

history.pushState({ ancre : ancre },title, adresse + ancre);
history.go(-1);
history.go(1);

Cette alternative a l'avantage de fonctionner sur Firefox Mac et Win, mais demande, pour donner un résultat sur Chrome, des délais entre les instructions :

setTimeout(function() {
 history.go(-1);
 setTimeout(function() {
  history.go(1);
 },44);
},44);

J'ai eu sur Chrome Mac et Win le résultat avec deux fois 44 ms, mais un réglage générique et fiable n'apparaît pas immédiatement.

Aucun commentaire: