Sélectionner par programme du texte dans un élément HTML modifiable par le contenu?

119

En JavaScript, il est possible de sélectionner par programme du texte dans un élément inputou textarea. Vous pouvez focaliser une entrée avec ipt.focus(), puis sélectionner son contenu avec ipt.select(). Vous pouvez même sélectionner une plage spécifique avec ipt.setSelectionRange(from,to).

Ma question est la suivante: y a-t-il un moyen de le faire également dans un contenteditableélément?

J'ai trouvé que je peux faire elem.focus(), mettre le curseur dans un contenteditableélément, mais par la suite, l'exécution elem.select()ne fonctionne pas (et ne fonctionne pas non plus setSelectionRange). Je ne trouve rien sur le Web à ce sujet, mais peut-être que je cherche la mauvaise chose ...

Au fait, si cela fait une différence, je n'en ai besoin que pour fonctionner dans Google Chrome, car c'est pour une extension Chrome.

callum
la source

Réponses:

170

Si vous souhaitez sélectionner tout le contenu d'un élément (modifiable ou non) dans Chrome, voici comment procéder. Cela fonctionnera également dans Firefox, Safari 3+, Opera 9+ (peut-être des versions antérieures aussi) et IE 9. Vous pouvez également créer des sélections jusqu'au niveau du personnage. Les API dont vous avez besoin sont DOM Range (la spécification actuelle est DOM Level 2 , voir aussi MDN ) et Selection, qui est spécifiée dans le cadre d'une nouvelle spécification de Range ( documents MDN ).

function selectElementContents(el) {
    var range = document.createRange();
    range.selectNodeContents(el);
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

var el = document.getElementById("foo");
selectElementContents(el);
Tim Down
la source
15
Pour plus de compatibilité, vous devez appeler selectElementContents()un setTimeout()ou requestAnimationFrame()s'il est appelé depuis un onfocus. Voir jsfiddle.net/rudiedirkx/MgASG/1/show
Rudie
@Dylan: Je ne suis pas sûr: la question mentionne que l'OP utilise déjà focus().
Tim Down
4
@Rudie Compatibilité pour quelle application?
yckart le
Fonctionne très bien sur le bureau. Sur les navigateurs mobiles, ne fonctionne pas. Aucune sélection effectuée. J'ai essayé Safari et Chrome sur iPhone iOS 11.
campbell
1
@campbell: Cela fonctionne sur Safari au moins sur iOS, à condition que vous ayez déjà une sélection . Sinon, le navigateur ne permet tout simplement pas à JavaScript d'afficher une sélection, probablement pour des raisons d'expérience utilisateur.
Tim Down
34

En plus de la réponse de Tim Downs , j'ai créé une solution qui fonctionne même dans oldIE:

var selectText = function() {
  var range, selection;
  if (document.body.createTextRange) {
    range = document.body.createTextRange();
    range.moveToElementText(this);
    range.select();
  } else if (window.getSelection) {
    selection = window.getSelection();
    range = document.createRange();
    range.selectNodeContents(this);
    selection.removeAllRanges();
    selection.addRange(range);
  }
};

document.getElementById('foo').ondblclick = selectText;​

Testé dans IE 8+, Firefox 3+, Opera 9+ et Chrome 2+. Même je l'ai configuré dans un plugin jQuery:

jQuery.fn.selectText = function() {
  var range, selection;
  return this.each(function() {
    if (document.body.createTextRange) {
      range = document.body.createTextRange();
      range.moveToElementText(this);
      range.select();
    } else if (window.getSelection) {
      selection = window.getSelection();
      range = document.createRange();
      range.selectNodeContents(this);
      selection.removeAllRanges();
      selection.addRange(range);
    }
  });
};

$('#foo').on('dblclick', function() {
  $(this).selectText();
});

... et qui est intéressé, voici la même chose pour tous les accros du café:

jQuery.fn.selectText = ->
  @each ->
    if document.body.createTextRange
      range = document.body.createTextRange()
      range.moveToElementText @
      range.select()
    else if window.getSelection
      selection = window.getSelection()
      range = document.createRange()
      range.selectNodeContents @
      selection.removeAllRanges()
      selection.addRange range
    return

Mettre à jour:

Si vous souhaitez sélectionner la page entière ou le contenu d'une région modifiable (signalée par contentEditable), vous pouvez le faire beaucoup plus simplement en basculant vers designModeet en utilisantdocument.execCommand :

Il y a un bon point de départ chez MDN et un peu de documentation .

var selectText = function () {
  document.execCommand('selectAll', false, null);
};

(fonctionne bien dans IE6 +, Opera 9+, Firefoy 3+, Chrome 2+) http://caniuse.com/#search=execCommand

yckart
la source
10

Étant donné que toutes les réponses existantes portent sur des divéléments, je vais vous expliquer comment procéder avec l' spanart.

Il existe une différence subtile lors de la sélection d'une plage de texte dans un fichier span. Pour pouvoir transmettre l'index de début et de fin du texte, vous devez utiliser un Textnœud, comme décrit ici :

Si le startNode est un nœud de type Text, Comment ou CDATASection, alors startOffset est le nombre de caractères à partir du début de startNode. Pour les autres types de nœuds, startOffset est le nombre de nœuds enfants entre le début de startNode.

var e = document.getElementById("id of the span element you want to select text in");
var textNode = e.childNodes[0]; //text node is the first child node of a span

var r = document.createRange();
var startIndex = 0;
var endIndex = textNode.textContent.length;
r.setStart(textNode, startIndex);
r.setEnd(textNode, endIndex);

var s = window.getSelection();
s.removeAllRanges();
s.addRange(r);
Domysee
la source
Cela devrait vraiment être: r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length); bien sûr, vous devriez vérifier que e.firstChild n'est pas nul.
Yorick
2
Il n'y a aucune différence entre faire une sélection dans un <div>et un <span>élément. Du moins, pas comme tu le décris.
Tim Down
Il existe des différences entre div et span, dans certains cas, une solution pour div ne fonctionne pas correctement en span. Par exemple, si vous sélectionnez du texte par programme avec la solution div puis collez le nouveau contenu, il ne remplacera pas tout le texte, seulement une partie et il y a des différences entre chrome et firefox
neosonne
6

Rangy vous permet de faire ce cross-browser avec le même code. Rangy est une implémentation multi-navigateur des méthodes DOM pour les sélections. Il est bien testé et rend cela beaucoup moins douloureux. Je refuse de toucher contenteditable sans lui.

Vous pouvez trouver rangy ici:

http://code.google.com/p/rangy/

Avec rangy dans votre projet, vous pouvez toujours écrire ceci, même si le navigateur est IE 8 ou antérieur et a une API native complètement différente pour les sélections:

var range = rangy.createRange();
range.selectNodeContents(contentEditableNode);
var sel = rangy.getSelection();
sel.removeAllRanges();
sel.addRange(range);

Où "contentEditableNode" est le nœud DOM qui a l'attribut contenteditable. Vous pourriez le récupérer comme ceci:

var contentEditable = document.getElementById('my-editable-thing');

Ou si jQuery fait déjà partie de votre projet et que vous le trouvez pratique:

var contentEditable = $('.some-selector')[0];
Tom Boutell
la source
Le projet Rangy a été déplacé vers Github maintenant: github.com/timdown/rangy
tanius
4

La façon moderne de faire les choses est comme ça. Plus de détails sur MDN

document.addEventListener('dblclick', (event) => {
  window.getSelection().selectAllChildren(event.target)
})
<div contenteditable="true">Some text</div>


la source
Merci, cela fonctionne très bien! Fwiw, cette page MDN marque cette technologie comme expérimentale. Mais cela fonctionne dans la version actuelle de Chrome et FF en juin 2020.
JohnK
2

[Mis à jour pour corriger l'erreur]

Voici un exemple adapté de cette réponse qui semble bien fonctionner dans Chrome - Sélectionnez la plage dans contenteditable div

var elm = document.getElementById("myText"),
    fc = elm.firstChild,
    ec = elm.lastChild,
    range = document.createRange(),
    sel;
elm.focus();
range.setStart(fc,1);
range.setEnd(ec,3);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

HTML est:

<div id="myText" contenteditable>test</div>
Patorjk
la source