J'ai utilisé John Ernest Je me suis servi de l'excellent code de l'auteur, et je l'ai un peu retravaillé pour mes besoins :
- Utilisation de TypeScript (dans une application Angular) ;
- En utilisant une structure de données légèrement différente.
Et en travaillant dessus, je suis tombé sur le peu connu (ou peu utilisé) TreeWalker, et j'ai encore simplifié le code, car il permet de se débarrasser de la récursivité.
Une optimisation possible pourrait consister à parcourir l'arbre une fois pour trouver le nœud de départ et le nœud d'arrivée, mais.. :
- Je doute que le gain de vitesse soit perceptible par l'utilisateur, même à la fin d'une page énorme et complexe ;
- Cela rendrait l'algorithme plus complexe et moins lisible.
Au lieu de cela, j'ai traité le cas où le début est le même que la fin (juste un signe d'insertion, pas de réelle sélection).
[EDIT] Il semble que les noeuds de la gamme sont toujours de type Text, donc j'ai simplifié le code un peu plus, et cela permet d'obtenir la longueur du noeud sans le couler.
Voici le code :
export type CountingState = {
countBeforeNode: number;
offsetInNode: number;
node?: Text; // Always of Text type
};
export type RangeOffsets = {
start: CountingState;
end: CountingState;
offsets: { start: number; end: number; }
};
export function isTextNode(node: Node): node is Text {
return node.nodeType === Node.TEXT_NODE;
}
export function getCaretPosition(container: Node): RangeOffsets | undefined {
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) { return undefined; }
const range = selection.getRangeAt(0);
const start = countUntilEndNode(container, range.startContainer as Text, range.startOffset);
const end = range.collapsed ? start : countUntilEndNode(container, range.endContainer as Text, range.endOffset);
const offsets = { start: start.countBeforeNode + start.offsetInNode, end: end.countBeforeNode + end.offsetInNode };
const rangeOffsets: RangeOffsets = { start, end, offsets };
return rangeOffsets;
}
export function setCaretPosition(container: Node, start: number, end: number): boolean {
const selection = window.getSelection();
if (!selection) { return false; }
const startState = countUntilOffset(container, start);
const endState = start === end ? startState : countUntilOffset(container, end);
const range = document.createRange(); // new Range() doesn't work for me!
range.setStart(startState.node!, startState.offsetInNode);
range.setEnd(endState.node!, endState.offsetInNode);
selection.removeAllRanges();
selection.addRange(range);
return true;
}
function countUntilEndNode(
parent: Node,
endNode: Text,
offset: number,
countingState: CountingState = { countBeforeNode: 0, offsetInNode: 0 },
): CountingState {
const treeWalker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);
while (treeWalker.nextNode()) {
const node = treeWalker.currentNode as Text;
if (node === endNode) {
// We found the target node, memorize it.
countingState.node = node;
countingState.offsetInNode = offset;
break;
}
// Add length of text nodes found in the way, until we find the target node.
countingState.countBeforeNode += node.length;
}
return countingState;
}
function countUntilOffset(
parent: Node,
offset: number,
countingState: CountingState = { countBeforeNode: 0, offsetInNode: 0 },
): CountingState {
const treeWalker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);
while (treeWalker.nextNode()) {
const node = treeWalker.currentNode as Text;
if (countingState.countBeforeNode <= offset && offset < countingState.countBeforeNode + node.length) {
countingState.offsetInNode = offset - countingState.countBeforeNode;
countingState.node = node;
break;
}
countingState.countBeforeNode += node.length;
}
return countingState;
}
0 votes
Regardez sa position dans le texte. Ensuite, recherchez la dernière occurrence de '@' avant cette position. Donc, juste un peu de logique de texte.
0 votes
De plus, je ne prévois pas d'autoriser d'autres balises à l'intérieur du <diV>, seulement du texte.
0 votes
Ok, oui je Je suis going to need other tags within the <div>. Il y aura des balises <a>, mais il n'y aura pas d'imbrication...
0 votes
@Bertvan : si le signe d'insertion est à l'intérieur d'un fichier
<a>
à l'intérieur de l'élément<div>
Quel décalage voulez-vous alors ? Le décalage dans le texte à l'intérieur de l'élément<a>
?1 votes
It should never be inside an <a> element. L'élément <a> doit être rendu en html, de sorte que l'utilisateur ne puisse pas réellement y placer le signe d'insertion.
0 votes
Avez-vous trouvé un moyen d'obtenir la position du caret autour des éléments enfants html ? J'essaie de résoudre le même problème :(
0 votes
Cette réponse pourrait vous aider. Le code est meilleur que toutes les réponses données ici. Je ne sais pas s'il fonctionne à 100% pour tout mais il fonctionne pour tout ce dont j'ai besoin. stackoverflow.com/a/64823701/3245937