2010-05-09 7 views
10

Je travaille sur un plugin jQuery qui va vous permettre de faire des tags de style @username, comme Facebook le fait dans leur boîte de saisie de statut. Mon problème est que, même après des heures de recherche et d'expérimentation, il semble VRAIMENT difficile de simplement déplacer le curseur. J'ai réussi à injecter l'étiquette <a> avec le nom de quelqu'un, mais en plaçant le curseur après il semble que la science de fusée, particulièrement si elle est supposée fonctionner dans tous les navigateurs.Auto-complétion par tag et mouvement curseur/curseur dans les éléments contentables

Et je l'ai même pas regardé en remplaçant le texte plutôt que encore @username tapé avec l'étiquette, juste injecter que je fais maintenant ... lol

Il y a une tonne de questions sur le travail avec contented ici sur Stack Overflow, et je pense que j'ai lu tous, mais ils ne couvrent pas vraiment ce dont j'ai besoin. Donc, toute autre information que tout le monde peut fournir serait géniale :)

+0

Avez-vous trouvé une autre explication jamais? J'ai posté une question similaire sur http://stackoverflow.com/questions/3764273/jquery-facebook-like-autosuggest-triggered-by et http://stackoverflow.com/questions/3972014/get-caret-position-in- contenteditable-div mais pas de chance ... – Bertvan

+0

Je peux certainement sympathiser avec un manque d'aide sur contenteditable! J'ai dû travailler beaucoup sur moi-même récemment. –

Répondre

1

Comme vous le dites, vous pouvez déjà insérer une balise sur le curseur, je vais commencer à partir de là. La première chose à faire est de donner un identifiant à votre tag lorsque vous l'insérez. Vous devriez avoir quelque chose comme ceci:

<div contenteditable='true' id='status'>I went shopping with <a href='#' id='atagid'>Jane</a></div>

Voici une fonction qui devrait placer le curseur juste après la balise.

function setCursorAfterA() 
{ 
    var atag = document.getElementById("atagid"); 
    var parentdiv = document.getElementById("status"); 
    var range,selection; 
    if(window.getSelection) //FF,Chrome,Opera,Safari,IE9+ 
    { 
     parentdiv.appendChild(document.createTextNode(""));//FF wont allow cursor to be placed directly between <a> tag and the end of the div, so a space is added at the end (this can be trimmed later) 
     range = document.createRange();//create range object (like an invisible selection) 
     range.setEndAfter(atag);//set end of range selection to just after the <a> tag 
     range.setStartAfter(atag);//set start of range selection to just after the <a> tag 
     selection = window.getSelection();//get selection object (list of current selections/ranges) 
     selection.removeAllRanges();//remove any current selections (FF can have more than one) 
     parentdiv.focus();//Focuses contenteditable div (necessary for opera) 
     selection.addRange(range);//add our range object to the selection list (make our range visible) 
    } 
    else if(document.selection)//IE 8 and lower 
    { 
     range = document.body.createRange();//create a "Text Range" object (like an invisible selection) 
     range.moveToElementText(atag);//select the contents of the a tag (i.e. "Jane") 
     range.collapse(false);//collapse selection to end of range (between "e" and "</a>"). 
     while(range.parentElement() == atag)//while ranges cursor is still inside <a> tag 
     { 
      range.move("character",1);//move cursor 1 character to the right 
     } 
     range.move("character",-1);//move cursor 1 character to the left 
     range.select()//move the actual cursor to the position of the ranges cursor 
    } 
    /*OPTIONAL: 
    atag.id = ""; //remove id from a tag 
    */ 
} 

EDIT: Testé et un script fixe. Cela fonctionne certainement dans IE6, chrome 8, firefox 4, et opera 11. Ne pas avoir d'autres navigateurs en main pour tester, mais il n'utilise pas de fonctions qui ont changé récemment, donc il devrait fonctionner dans tout ce qui supporte contenteditable.

Ce bouton est pratique pour les tests: <input type='button' onclick='setCursorAfterA()' value='Place Cursor After &lt;a/&gt; tag' >

Nico

5

Vous pouvez utiliser my Rangy library, qui tente avec un certain succès pour normaliser les implémentations de gamme de navigateur et de sélection. Si vous avez réussi à insérer le <a> comme vous le dites et vous avez dans une variable appelée aElement, vous pouvez effectuer les opérations suivantes:

var range = rangy.createRange(); 
range.setStartAfter(aElement); 
range.collapse(true); 
var sel = rangy.getSelection(); 
sel.removeAllRanges(); 
sel.addRange(range); 
+1

+1 pour la bibliothèque, ce sera une aide précieuse – Bertvan

+0

Votre question m'a intéressé et j'ai écrit un code générique pour faire ce genre de chose. Je posterai bientôt. –

3

Je me suis intéressé à cela, donc je l'ai écrit le départ point pour une solution complète. Ce qui suit utilise mon Rangy library avec son selection save/restore module pour enregistrer et restaurer la sélection et normaliser les problèmes croisés. Il entoure tout le texte correspondant (@whatever dans ce cas) avec un élément de lien et positionne la sélection là où elle avait été précédemment. Ceci est déclenché après l'absence d'activité du clavier pendant une seconde. Cela devrait être assez réutilisable.

function createLink(matchedTextNode) { 
    var el = document.createElement("a"); 
    el.style.backgroundColor = "yellow"; 
    el.style.padding = "2px"; 
    el.contentEditable = false; 
    var matchedName = matchedTextNode.data.slice(1); // Remove the leading @ 
    el.href = "http://www.example.com/?name=" + matchedName; 
    matchedTextNode.data = matchedName; 
    el.appendChild(matchedTextNode); 
    return el; 
} 

function shouldLinkifyContents(el) { 
    return el.tagName != "A"; 
} 

function surroundInElement(el, regex, surrounderCreateFunc, shouldSurroundFunc) { 
    var child = el.lastChild; 
    while (child) { 
     if (child.nodeType == 1 && shouldSurroundFunc(el)) { 
      surroundInElement(child, regex, surrounderCreateFunc, shouldSurroundFunc); 
     } else if (child.nodeType == 3) { 
      surroundMatchingText(child, regex, surrounderCreateFunc); 
     } 
     child = child.previousSibling; 
    } 
} 

function surroundMatchingText(textNode, regex, surrounderCreateFunc) { 
    var parent = textNode.parentNode; 
    var result, surroundingNode, matchedTextNode, matchLength, matchedText; 
    while (textNode && (result = regex.exec(textNode.data))) { 
     matchedTextNode = textNode.splitText(result.index); 
     matchedText = result[0]; 
     matchLength = matchedText.length; 
     textNode = (matchedTextNode.length > matchLength) ? 
      matchedTextNode.splitText(matchLength) : null; 
     surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true)); 
     parent.insertBefore(surroundingNode, matchedTextNode); 
     parent.removeChild(matchedTextNode); 
    } 
} 

function updateLinks() { 
    var el = document.getElementById("editable"); 
    var savedSelection = rangy.saveSelection(); 
    surroundInElement(el, /@\w+/, createLink, shouldLinkifyContents); 
    rangy.restoreSelection(savedSelection); 
} 

var keyTimer = null, keyDelay = 1000; 

function keyUpLinkifyHandler() { 
    if (keyTimer) { 
     window.clearTimeout(keyTimer); 
    } 
    keyTimer = window.setTimeout(function() { 
     updateLinks(); 
     keyTimer = null; 
    }, keyDelay); 
} 

HTML:

<p contenteditable="true" id="editable" onkeyup="keyUpLinkifyHandler()"> 
    Some editable content for @someone or other 
</p> 
+0

c'est génial. J'essaie d'étendre cela en utilisant rangy pour mettre en évidence les URL que vous tapez. Cependant, il semble y avoir un certain nombre de problèmes - une fois que le lien est détecté, il ne peut pas être modifié, et la définition de contentEditable à true à l'intérieur de la balise A brise également les choses. Y a-t-il une meilleure approche pour cela? –

+0

@MattRoberts: Je ne pense pas qu'il y ait une approche fondamentalement meilleure, juste une meilleure implémentation. Cela a été conçu comme un point de départ vraiment. –

+0

Merci Tim. Je me demandais si votre nouveau module de mise en évidence convenait à ce genre de tâche? –

Questions connexes