Déterminez l'index de position d'un caractère dans un élément HTML lorsque vous cliquez

J'ai un élément HTML avec seulement un texte visible à l'intérieur. Cet exemple est un élément <div> , mais il peut s'agir d'un <span> , <p> ou d'un autre élément DOM.

 <div>This is a simple example.</div> 

Lorsque vous cliquez dessus, je peux obtenir la position du curseur sur la surface du div, mais je dois déterminer la position du personnage le plus proche et / ou son index dans la chaîne div.innerHTML au moment du clic.

J'ai trouvé une implémentation similaire dans la méthode "getCharNumAtPosition" dans les entités de texte SVG ici .

Est-il possible d'implémenter une telle fonction dans JavaScript qui fonctionne avec HTML?

(Les solutions seraient très utiles si elles sont portables dans la plupart des navigateurs modernes, fonctionnent avec la plupart des langues écrites et sont basées sur des normes relativement stables afin qu'elles ne deviennent plus buggées plus tard).

 $('div').click( function () { getSelectionPosition (); }); function getSelectionPosition () { var selection = window.getSelection(); console.log(selection.focusNode.data[selection.focusOffset]); alert(selection.focusOffset); } 

Cela fonctionne avec "clic", ainsi que avec une "gamme" pour la plupart des navigateurs. ( selection.type = "caret" / selection.type = "range" ).

selection.focusOffset() vous donne la position dans le nœud interne . Si les éléments sont imbriqués, dans les balises <b> ou <span> par exemple, il vous donnera la position à l'intérieur de l'élément interne, pas le texte intégral, plus ou moins. Je ne peux pas "sélectionner" la première lettre d'une sous-étiquette avec le focusOffset et le type "caret" (clic, pas sélection de gamme). Lorsque vous cliquez sur la première lettre, elle donne la position du dernier élément avant le début du tag plus 1. Lorsque vous cliquez sur la deuxième lettre, il vous donne "1" correctement. Mais je n'ai pas trouvé un moyen d'accéder au premier élément (décalage 0) de l'élément secondaire. Cette "sélection / gamme" des choses semble buggy (ou très intuitive pour moi). ^^

Mais c'est assez simple à utiliser sans éléments imbriqués! (Fonctionne bien avec votre <div> )

Voici un violon

Edition importante 2015-01-18:

Cette réponse a répondu quand il a été accepté, mais pas plus, pour les raisons indiquées ci-dessous. D'autres réponses sont maintenant très utiles.

  • La réponse générale de Matthew
  • L'exemple de travail fourni par Douglas Daseeco.

Le comportement de window.getSelection() Firefox et Chrome débogué. Malheureusement, il est maintenant inutile pour ce cas d'utilisation. (La documentation de lecture, IE 9 et au-delà doit comporter la même chose).

Maintenant, le milieu d'un personnage est utilisé pour décider du décalage. Cela signifie que cliquer sur un personnage peut donner 2 résultats. 0 ou 1 pour le premier personnage, 1 ou 2 pour le deuxième, etc.

J'ai mis à jour l' exemple de JSFiddle .

Notez que si vous redimensionnez la fenêtre ( Ctrl + souris), le comportement est assez bogue sur Chrome pour quelques clics.

Cet exemple est un chargement rapide pour des blocs de texte de taille modérée, portables et robustes. Bien que son élégance ne soit pas immédiatement apparente, la fiabilité réside dans la création d'une simple correspondance entre les personnages internationaux et les auditeurs de l'événement DOM 1 .

Il est préférable de s'appuyer uniquement sur les normes de codage de caractères, DOM et ECMA largement adoptées comme dans cet exemple. D'autres approches s'appuient souvent sur des attributs ou des fonctions de cible d'événement de bas niveau comme getSelection (), qui ne sont ni stables au fil du temps ni portables sur les navigateurs et les langues 2 .

Le code d'exemple pourrait être modifié de plusieurs façons pour des applications spécifiques. Par exemple, d'autres mécanismes pourraient être utilisés pour sélectionner les objets DOM initialisés pour les rappels de charClick. Les paires de substitution peuvent également être prises en charge dans la boucle i.

  <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Character Click Demo</title> <script type='text/javascript'> var pre = "<div onclick='charClick(this, "; var inf = ")'>"; var suf = "</div>"; function charClick(el, i) { var p = el.parentNode.id; var s = "para '" + p + "' idx " + i + " click"; ele = document.getElementById('result'); ele.innerHTML = s; } function initCharClick(ids) { var el; var from; var length; var to; var cc; var idArray = ids.split(" "); var idQty = idArray.length; for (var j = 0; j < idQty; ++ j) { el = document.getElementById(idArray[j]); from = unescape(el.innerHTML); length = from.length; to = ""; for (var i = 0; i < length; ++ i) { to = to + pre + i + inf + from.charAt(i) + suf; el.innerHTML = to; } } </script> <style> .characters div { padding: 0; margin: 0; display: inline } </style> </head> <body class='characters' onload='initCharClick("h1 p0 p2")'> <h1 id='h1'>Character Click Demo</h1> <p id='p0'>Test &#xE6;&#x20AC; &ndash; &#xFD7;&#xD8; &mdash; string.</p> <p id='p1'>Next 𐐷 😀E para.</p> <p id='p2'>&copy; 2017</p> <hr> <p id='result'>&nbsp;</p> </body> </html> 

Des tests complets sont recommandés avant de l'utiliser dans des scénarios de production internationalisés. Si des bugs sont détectés, suggérer des modifications ou ajouter des commentaires afin que des raffinements puissent être ajoutés.


Remarques

[1] Ces auditeurs d'événements auraient pu être ajoutés directement, mais nécessiteraient une ramification pour la portabilité du navigateur ou le poids supplémentaire des bibliothèques qui n'ajoutent aucune vitesse particulière ou valeur de commodité pour ce cas d'utilisation.

[2] Voir les notes sur les événements sélectionnés dans la page des événements UI du W3C .

Vous pouvez, en utilisant JavaScript, briser chaque personnage dans sa propre balise SPAN et ajouter un événement onclick pour chaque. Ensuite, lisez la position x, y à partir des données de l'événement lorsque le SPAN est cliqué.

De plus, vous aurez accès au caractère cliqué avec event.target.innerHTML