Gérer plusieurs onglets à partir d'une application WEB, casse tête sans bonne solution ?
Bonjour,
J'ai précédemment ouvert une discussion sur les fonctions javascript propres aux navigateurs qui permettent de gérer fenêtres ou onglets.
L'écho a été faible.
Je reviens sur le sujet, mais sous un autre angle, poser le problème global fonctionnel en faisant appel aux réponses et idées de chacun (même si j'ai bossé très longtemps ces sujets sans avoir pu apporter de réponse que je juge satisfaisante). Et l'on verra ensuite le problème des outils nécessaires et utiles.
Le contexte (je prends le cas Wordpress pour ceux qui connaissent l'outil, mais ma réflexion est applicable à tous les gestionnaires de contenu):
Nous avons un blog (Wordpress) qui doit supporter des documents longs (plusieurs dizaines d'équivalent pages de livre soit à partir à peu près de 400 lignes de texte, long article jusqu'à 5 000 et plus, essais, livres...), Wordpress prévoit alors de les découpe en "pages" que je préfère appeler "sections" qui ont une adresse différente (c'est le nom que j'ai choisi dans mes docs pour éviter les confusions de langage). (voir note sur la manière de découper le texte dans wordpress)
Le problème
Le problème que je pose est : Comment fait-on, pendant la lecture du texte principal, pour consulter par exemple la bibliographie (ou tout autre "section" du même type, index, table des matières générale) sans qu'un nouvel onglet soit ouvert à chaque fois que l'on veut accéder à ces "sections" ?
Bien entendu il faut conserver les avantages fondamentaux des pages et articles Wordpress (ou autre gestionnaires de contenu HTML) en matière de gestion de liens, d'introduction de multimédia etc...
Le comportement de l'application que l'on attend (à la base) :
- Quand l'on clique sur un élément bibliographique, la première fois un nouvel onglet s'ouvre, mais ensuite l'onglet bibliographie déjà ouvert va, tout en récupérant le focus, se positionner sur l'item du lien cliqué.
- On peut alors revenir au point de lecture en cours sur l'onglet principal.
- Le sommaire ou table des matières permet de se positionner en un click à un endroit quelconque du document global
On peut aller bien plus loin dans les fonctions mais le mieux est, je pense, de s'arrêter à ça dans un premier temps.
Il va de soit que notre "outil" doit résister aux fermetures intempestives, à l'arrêt accidentel, aux duplications d'onglets ou déplacement vers d'autres fenêtres, toutes fonctions autorisées par les navigateurs ou leurs plugins.
Je veux bien, in fine, ajouter la fonction plus générale : consulter plusieurs sections (chapitres d'un document) simultanément, et accéder à l'élément d'index, de la bibliographie etc. uniques en un click, ça peut être bien pratique pour étudier un document, n'est ce pas ce que l'on fait avec un document papier, non ?
Quand on a résolu les problèmes fondamentaux de manipulation de fenêtres et d'onglets, il n'y a en fait plus de limites aux développements ergonomiques.
Voilà, j'ai fini.
Merci d'avance pour vos idées et questions.
Bien cordialement
Trebly
note : On obtient le découpage en introduisant un pseudo balise "<!--nextpage-->", des plugins annexes permettent de nommer les sections et de générer un menu des "sections" - pour un essai, par exemple, ce seront des Chapitres...et à la fin les section particulières que sont index, bibliographie, table des matières générale etc..
Que c'est bien de ne pas travailler tout seul, on avance, mes réponses
Bonjour,
Bon, comme j'ai beaucoup travaillé le sujet depuis près de deux ans, j'ai déjà beaucoup de réponses et des commentaires à faire sur chacune de tes réflexions et remarques.
Citation:
Je crois que je commence à comprendre. À cause des différences d’implémentation, le recours à open pour reconstruire des références est à la fois non fiable et instable. Il faut donc mettre en œuvre une solution moins hacky, qui n’appelle open que quand on est sûr du résultat, autrement dit en se limitant à la fonctionnalité dont le support est garanti partout.
Tout à fait d'accord, ceci étant, pousser à ce que window.open assure un service standard de base ne me parait pas inutile même si c'est à long terme. En effet à propos de ta proposition finale, malgré son intérêt sur le plan formel, je suis presque certain, que l'on butera à nouveau sur la même question : récupérer le handle sur des fenêtres nommées dans un domaine et je crains qu'il n'y ait pas de solution en dehors de ma proposition sur window.open (voir détail plus loin).
Citation:
Quand j’ai parlé de postMessage j’avais presque oublié l’existence de localStorage. Effectivement, si tu n’as pas de problèmes de domaine/origine, utiliser localStorage est plus simple. À ce propos, je ne crois pas qu’il soit judicieux de mélanger les cookies localStorage, ce dernier pouvant être vu comme une amélioration des premiers.
C'est bien ce que j'avais pensé. Ceci étant le(s) cookies (j'en ai 3) ne conservent pas de données, mais uniquement les méta-données (y compris sur les local storages) : droits, marqueurs dates, dates limites, validité etc... Je n'ai pas vu provisoirement l'utilité d'utiliser un localstorage dédié remplaçant les cookies. Le droit ouvert aux cookies pour le site par l'utilisateur étant par ailleurs le sésame (voir note 2).
Citation:
J’imagine une solution basée justement sur localStorage. Il servirait de canal de communication entre les différentes fenêtres. Chaque fenêtre se verrait attribuer un id ad hoc, je veux dire par là pas un handle, mais quelque chose qui peut se sérialiser (Il faudra réfléchir à la façon d’attribuer cet id. Dans le scope JavaScript de la page elle-même, pas pérenne. En dur dans le code HTML, cela demanderait l’implication du serveur. À creuser.)
Bien d'accord, c'est ce que j'ai fait. Chaque fenêtre qui prend le focus, nouvel enfant ou existante, prend le contrôle du localstorage pendant que celui qui l'a perdu en a fait la mise à jour, puis il le modifie avec un timer à partir d'un objet local (voir note 1) pendant que les opérations exécutées affectent l'objet conservé en mémoire. Une fenêtre nouvelle du domaine non enfant prend le contrôle en l'état.
Pour le id (window.name) des "data-*" sont récupérées dans un div cachée dans le html en tête de document. Ces data alimentent le gestionnaire de noms qui sont les identifiants d'onglets. Le gestionnaire de noms identifiants (une lib dans un objet) s'occupe de tout.
Néanmoins, s'il est possible de gérer correctement ces id dans l'objet il n'est pas possible d'assurer totalement la synchronisation et interdire totalement la production de doublons de noms (note 3), le gestionnaire nettoye.
Citation:
Une table des ids serait maintenue dans le localStorage, et à chaque id serait associée une file de messages. Chaque page consulterait sa propre file à intervalles réguliers (système de polling). Il y aurait également une file spéciale * pour les messages adressés à toutes les pages (broadcast).
Là je ne fais pas comme ça. Comme dit plus haut, comme une seule à le focus à la fois, tout le monde partage la liste de ce qui a été ouvert et référencé (et fait les purges utiles automatiquement). Cela marche bien, on sait quels onglets sont ouvert (ou ont été) car c'est le récupération des handles qui rend impossible le fonctionnement.
En effet comme il est impossible de récupérer les handles de manière fiable, on ne peut jamais être sur que, depuis la liste (manager des onglets ouverts et de l'historique) ou lors de l'appel via d'un lien contenu où que ce soit :
on n'ouvrira pas un nouvel onglet alors qu'il l'est déjà (avec le risque d'incohérence si des données sont en train d'être saisies : annotations, marques pages, commentaires...) et ceci bien que ces données soient aussi sauvées en localstorage (note 4).
Citation:
Une application de ces messages serait de vérifier si une page donnée existe toujours. La page mère émettrait le message « est-ce que tu existes toujours » à une page fille, et attendrait la réponse un temps suffisamment long (par exemple, si l’intervalle de polling est de 100 ms, une attente de 300 ms me paraît raisonnable) avant de faire les opérations nécessaires. Bien sûr il faudra s’occuper de l’intégrité de la table.
Proposition fort intéressante. On peut créer ainsi une fonction remplaçante de window.open(check). Quant à l'opération de contrôle d'intégrité elle existe déjà dans mon développement.
Malheureusement cela ne règle toujours pas la question d'origine : comment dans un même domaine à partir de n'importe quel onglet toujours pouvoir accéder, passer le focus, à un onglet que l'on sait être ouvert sans risquer de l'ouvrir en double.
Depuis près d'un an et demi, j'ai pu, avec chrome, à partir d'un onglet ouvert pour un chapitre (section 1) de document long, accéder de manière répétitive à un onglet "bibliographie" (section 11) en me positionnant dans chaque "fragment" utile, en ayant ouvert la bibliographie à partir de ce chapitre.
Mais comme on va le comprendre c'est un cas très spécifique non généralisable.
En effet si l'on ouvre le chapitre suivant du texte, ce chapitre étant un onglet frère (section 3) de la bibliographie (section 11), ne pourra récupérer le handle de ce frère bibliographie (section 11) et l'activation d'un lien vers la bibliographie ouvrira immanquablement une nouvelle instance de la bibliographie (avec chrome c'est sur, vérification en cours sur d'autres navigateurs... mais cela n'apporte que de l'eau à mon moulin).
Bien, tout en rédigeant je crois que je tiens quelque chose : Après chaque création d'onglet enfant, il peut récupérer normalement le handle du parent ou de l'opener (et je ne m'en suis pas servi jusqu'à présent). On deviendrai donc capables (voir ma note 1) de reconstituer la famille de handles.
J'attaque le sujet... (voir note 5). Echec.
Le problème est, me semble-t-il insoluble en l'état de la technique. J'ai perdu beaucoup de temps (note 6) pour un résultat bien maigre (mais prêt pour le futur...).
Le problème d'ergonomie que je ne résous pas est que, très rapidement, quand je teste la consultation du site que je tente d'ouvrir, après un demi heure de (re)lecture d'un texte de 100 pages en dix chapitres, je me trouve, alors que je vérifie les liens vers une bibliographie, avec un grand nombre d'onglets ouverts très souvent en double rendant le travail inextricable (note 7). Seul j'y arrive mais les tests effectués avec des relecteurs (j'ai plus de mille pages à publier) sont un échec, d'où mon développement. Et je ne vois vraiment pas quel outil utiliser compte tenu de l'investissement déjà réalisé.
Nota (rappel) : ça serait tout de même sacrément simple d'avoir l'instruction "window.open('',<name>,'check=yes')" qui retournerait le handle de la fenêtre (ou onglet) <name> connue dans le domaine ou null si elle n'existe pas...
et que, au moins, focus() puisse être exécuté pour n'importe quel handle acquis sur un des élément inclus dans la famille de fenêtres.
(voir note 8).
Je n'arrive toujours pas à comprendre pourquoi si peu de gens s'intéressent à ce sujet. Tu est le seul qui ait compris, je crois bien, l'importance de la question.
C'est je pense le clef de la consultation de documents longs autrement qu'en pdf.
Je dois, peut-être, me poser des questions avec dix ans d'avance...
Cordialement
Trebly
_____________________________________________________
note 1 :
Dans la pratique je gère un objet principal "tabs" qui comprend deux sous objets qui sont des array d'objets
- l'array des références de tab (ouvertes et historique)
- l'array des handles correspondant à des onglets ouverts
Quand un onglet est ouvert avec un handle connu une double relation 1-1 est établie (index réciproques)
La première est l'équivalent d'un historique dans lequel les onglets ouverts sont référencés comme tels donnant accès au handle
Le deuxième est une table des onglets ouverts du domaine pointant vers la première
Toute anomalie est soit résolue soit purgée, seul l'élément historique est alors conservé.
Malheureusement cela ne fonctionne que pour des liens parent-enfant gérés via window.open et avec chrome.
______________________________________________________
note 2 : le problème du blocage des cookies : s'il existe des plugins pour détecter et valider l'autorisation de l'utilisation des cookies avec WP, rien n'est dit (je n'ai pas trouvé) sur les droits à utiliser le localstorage. Pour l'instant j'ai considéré que l'autorisation donnée par l'utilisateur d'utiliser les cookies était valable pour le local storage, je verrai dans les test (il y a tellement de variantes).
______________________________________________________
note 3 : car il ne faut pas oublier que les ouvertures, fermetures etc... sont asynchrones et que beaucoup de choses peuvent se produire pendant qu'un onglet se recharge ou qu'un nouvel onglet s'initialise. Les mises à jour lors des changements de focus arrivent à stabiliser partiellement l'état. Le seul "ticket" valide serait mon "window.open('',<...>,'check=yes')".
______________________________________________________
note 4 : Leur récupération en cas d'ouverture en double (double de l'url, pas nécessairement de nom) est assurée (autres localstorages) mais cela devient une vraie usine à gaz (en faire une API stable ou des plugins WordPress me parait impossible). Local ou ajax et base de données cela aboutit à afficher n fois le même contenu, sans pouvoir maintenir l'unicité autrement que manuellement (grâce à la liste gérée et affichée qui ne peut même pas dire si un onglet est ouvert ni y accèder) c'est tout simplement absurde.
_____________________________________________________
note 5 : Test de l'accès aux handles de fenêtres par leur nom (window.open("",<name>,"")) dans un domaine
Réponse (partielle mais suffisante):
Chrome ne connait que la filiation via window.open et l'opener est l'onglet (la fenêtre) qui a exécuté window.open.
Il n'est possible d'accéder à une fenêtre par son nom (via wx=window.open('',<name>,'') que si l'onglet qui exécute l'instruction est celui qui a ouvert l'onglet, donc qui connait en principe déjà le handle.
wx.focus() passera le focus à cet enfant généré via window.open, mais l'inverse est impossible.
Appairées (chacun a le handle sur l'autre) les deux onglets connaissent réciproquement et peuvent manipuler leurs données.
Par contre tout enfant généré par un lien standard restera inconnu et inatteignable (opener non défini et parent est "self")...
Firefox reconnait, lui, toutes les fenêtres du domaine (et l'on accède ou modifie les données), un point important correspondant à la doc mozilla sur window.open, par contre les commandes <element>.focus() sont ignorées ce qui rend impossible l'exécution des fonctions souhaitées.
________________________________________________________
note 6 : j'ai passé entre 1000 et 2000 heures sur le développement, tout n'est pas perdu loin de là mais pour le résultat obtenu il y a dix fois trop de temps passé. Mais j'ai commis l'erreur de ne pas faire suffisamment de test élémentaires de toutes les configurations et de prendre les docs comme du bon pain sans tout vérifier. Ensuite j'ai développé sans me rendre compte que je développais une bibliothèque correspondant au seul cas qui marche et sur lequel j'effectuais en fait les test. Puis in fine, la bibliothèque fonctionnant assez bien quand j'ai étendu les cas d'ouverture d'onglets par d'autre moyens que window.open (très vite complexes) j'ai cherché des anomalies dans mon code... puis les test sur les différents navigateurs ont fait apparaitre des différences...
________________________________________________________
note 7 : très vite une trentaine d'onglets ouverts dans un travail de correction et de mise à jour de liens concerant deux ou trois chapitres sur dix (onglets auxquels s'ajoutent les pages d'édition des chapitres en cours de modifs)? Cela devient vite inextricable, alors que quatre suffiraient (complétés de quatre pages de modifs sur une fenêtre séparée).
Le problème est que, un même onglet peut être requis à partir, à la fois de plusieurs moyens (lien simple, duplication ou ouverture manuelle, window.open lancé par un opener - seul cas qui fonctionne -, etc...) et à partir de plusieurs documents (chapitres qui consultent la même bibliographie), et presqu'à chaque fois la même url est réouverte dans un nouvel onglet et si l'on veut gérer manuellement l'on risque de fermer celle qui marche (qui est capable de récupérer le focus) sans pouvoir du tout rétablir la hiérarchie des onglets ouverts.
________________________________________________________
note 8 : dans ce cas l'établissement d'un tableau des onglets ouverts (relation 1-1 entre noms uniques et handles) et partagé entre les onglets du même domaine devient presque immédiat et tous les problèmes tordus tombent d'un seul coup.