Sélection de texte dans un élément (semblable à la mise en évidence avec votre souris)

424

J'aimerais que les utilisateurs cliquent sur un lien, puis il sélectionne le texte HTML dans un autre élément ( pas une entrée).

Par «sélectionner», j'entends la même manière que vous sélectionneriez du texte en faisant glisser votre souris dessus. Cela a été un ours pour la recherche car tout le monde parle de «sélectionner» ou de «mettre en évidence» en d'autres termes.

Est-ce possible? Mon code jusqu'à présent:

HTML:

<a href="javascript:" onclick="SelectText('xhtml-code')">Select Code</a>
<code id="xhtml-code">Some Code here </code>

JS:

function SelectText(element) {
    $("#" + element).select();
}

Suis-je en train de manquer quelque chose d’évident?

Jason
la source

Réponses:

613

Javascript simple

function selectText(node) {
    node = document.getElementById(node);

    if (document.body.createTextRange) {
        const range = document.body.createTextRange();
        range.moveToElementText(node);
        range.select();
    } else if (window.getSelection) {
        const selection = window.getSelection();
        const range = document.createRange();
        range.selectNodeContents(node);
        selection.removeAllRanges();
        selection.addRange(range);
    } else {
        console.warn("Could not select text in node: Unsupported browser.");
    }
}

const clickable = document.querySelector('.click-me');
clickable.addEventListener('click', () => selectText('target'));
<div id="target"><p>Some text goes here!</p><p>Moar text!</p></div>
<p class="click-me">Click me!</p>

Voici une démo fonctionnelle . Pour ceux d'entre vous qui recherchent un plugin jQuery, j'en ai fait un aussi .


jQuery (réponse originale)

J'ai trouvé une solution pour cela dans ce fil . J'ai pu modifier les informations fournies et les mélanger avec un peu de jQuery pour créer une fonction totalement géniale pour sélectionner le texte dans n'importe quel élément, quel que soit le navigateur:

function SelectText(element) {
    var text = document.getElementById(element);
    if ($.browser.msie) {
        var range = document.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if ($.browser.mozilla || $.browser.opera) {
        var selection = window.getSelection();
        var range = document.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);
    } else if ($.browser.safari) {
        var selection = window.getSelection();
        selection.setBaseAndExtent(text, 0, text, 1);
    }
}
Jason
la source
La solution jQuery me donne un TypeError non capturé: Impossible de lire la propriété 'msie' d'un indéfini
egmfrs
@egmfrs oui depuis que cette réponse a été publiée, jquery a supprimé son browserrenifleur.
Jason
127

Voici une version sans reniflement de navigateur et sans recours à jQuery:

function selectElementText(el, win) {
    win = win || window;
    var doc = win.document, sel, range;
    if (win.getSelection && doc.createRange) {
        sel = win.getSelection();
        range = doc.createRange();
        range.selectNodeContents(el);
        sel.removeAllRanges();
        sel.addRange(range);
    } else if (doc.body.createTextRange) {
        range = doc.body.createTextRange();
        range.moveToElementText(el);
        range.select();
    }
}

selectElementText(document.getElementById("someElement"));
selectElementText(elementInIframe, iframe.contentWindow);
Tim Down
la source
existe-t-il un moyen de sélectionner le texte d'un div contentEditable='true'?
oldboy
@PrimitiveNom: Ce code fonctionnera sur un élément modifiable, mais vous devrez vous assurer qu'il a le focus en premier.
Tim Down
18

Le code de Jason ne peut pas être utilisé pour des éléments à l'intérieur d'un iframe (car la portée diffère de la fenêtre et du document). J'ai corrigé ce problème et je l'ai modifié afin d'être utilisé comme n'importe quel autre plugin jQuery (chaînable):

Exemple 1: Sélection de tout le texte à l'intérieur des balises <code> en un seul clic et ajoutez la classe "selected":

$(function() {
    $("code").click(function() {
        $(this).selText().addClass("selected");
    });
});

Exemple 2: Lors d'un clic sur le bouton, sélectionnez un élément dans un iframe:

$(function() {
    $("button").click(function() {
        $("iframe").contents().find("#selectme").selText();
    });
});

Remarque: n'oubliez pas que la source iframe doit résider dans le même domaine pour éviter les erreurs de sécurité.

Plugin jQuery:

jQuery.fn.selText = function() {
    var obj = this[0];
    if ($.browser.msie) {
        var range = obj.offsetParent.createTextRange();
        range.moveToElementText(obj);
        range.select();
    } else if ($.browser.mozilla || $.browser.opera) {
        var selection = obj.ownerDocument.defaultView.getSelection();
        var range = obj.ownerDocument.createRange();
        range.selectNodeContents(obj);
        selection.removeAllRanges();
        selection.addRange(range);
    } else if ($.browser.safari) {
        var selection = obj.ownerDocument.defaultView.getSelection();
        selection.setBaseAndExtent(obj, 0, obj, 1);
    }
    return this;
}

Je l'ai testé dans IE8, Firefox, Opera, Safari, Chrome (versions actuelles). Je ne sais pas si cela fonctionne dans les anciennes versions d'IE (sincèrement, je m'en fiche).

lepe
la source
3
$ .browser est maintenant obsolète / supprimé - cela nécessite une réécriture
James McCormack
2
@JamesMcCormack: oui. Je ne sais pas si la réécriture en vaudra la peine car il existe d'autres solutions publiées ici qui n'impliquent pas $ .browser.
lepe
16

Ce fil contient des trucs vraiment merveilleux. Mais je ne suis pas en mesure de le faire directement sur cette page en utilisant FF 3.5b99 + FireBug en raison d'une "erreur de sécurité".

Yipee !! J'ai pu sélectionner toute la barre latérale droite avec ce code, j'espère qu'il vous aidera:

    var r = document.createRange();
    var w=document.getElementById("sidebar");  
    r.selectNodeContents(w);  
    var sel=window.getSelection(); 
    sel.removeAllRanges(); 
    sel.addRange(r); 

PS: - Je n'ai pas pu utiliser les objets retournés par les sélecteurs jquery comme

   var w=$("div.welovestackoverflow",$("div.sidebar"));

   //this throws **security exception**

   r.selectNodeContents(w);
TheVillageIdiot
la source
2
Vous devez obtenir l'élément de jQuery, car vous essayez de sélectionner un objet jQuery: var w = $ ("div.welovestackoverflow", $ ("div.sidebar")). Get (0);
Blixt
ne fonctionne pas ... j'obtiens une erreur "l'objet ne prend pas en charge cette méthode" et il met en évidence la première ligne. j'ai fait quelques recherches et j'ai trouvé qu'il y avait un "document.body.createTextRange ()" mais ensuite "selectNodeContents" ne fonctionne pas .... et c'est dans IE
Jason
j'ai lu ce fil que vous avez trouvé ... incroyable ... j'ai pu créer une fonction à partir de ces informations qui fonctionne sur tous les navigateurs. Merci beaucoup! Ma solution est affichée
Jason
6

Vous pouvez utiliser la fonction suivante pour sélectionner le contenu de n'importe quel élément:

jQuery.fn.selectText = function(){
    this.find('input').each(function() {
        if($(this).prev().length == 0 || !$(this).prev().hasClass('p_copy')) { 
            $('<p class="p_copy" style="position: absolute; z-index: -1;"></p>').insertBefore($(this));
        }
        $(this).prev().html($(this).val());
    });
    var doc = document;
    var element = this[0];
    console.log(this, element);
    if (doc.body.createTextRange) {
        var range = document.body.createTextRange();
        range.moveToElementText(element);
        range.select();
    } else if (window.getSelection) {
        var selection = window.getSelection();        
        var range = document.createRange();
        range.selectNodeContents(element);
        selection.removeAllRanges();
        selection.addRange(range);
    }
};

Cette fonction peut être appelée comme suit:

$('#selectme').selectText();
Wolfack
la source
5

Je cherchais la même chose, ma solution était la suivante:

$('#el-id').focus().select();
Auston
la source
3
vous ne pouvez pas utiliser focus()sur une non-entrée, c'est de quoi traite cette question.
Jason
4
mais vous pouvez l'utiliser sur un élément textarea - c'est le problème que j'ai recherché sur Google pour arriver ici. Ma faute de ne pas avoir lu la question tout au long.
Auston
4

J'ai aimé la réponse de Lepe sauf pour quelques choses:

  1. Navigateur reniflant, jQuery ou non n'est pas optimal
  2. SEC
  3. Ne fonctionne pas dans IE8 si le parent d'obj ne prend pas en charge createTextRange
  4. La capacité de Chrome à utiliser setBaseAndExtent devrait être optimisée (IMO)
  5. Ne sélectionnera pas le texte s'étendant sur plusieurs éléments DOM (éléments au sein de l'élément "sélectionné"). En d'autres termes, si vous appelez selText sur un div contenant plusieurs éléments span, il ne sélectionnera pas le texte de chacun de ces éléments. C'était une rupture pour moi, YMMV.

Voici ce que j'ai trouvé, avec un clin d'œil à la réponse de Lepe pour l'inspiration. Je suis sûr que je serai ridiculisé car c'est peut-être un peu lourd (et pourrait en fait être plus, mais je m'égare). Mais cela fonctionne et évite de flairer le navigateur et c'est le point .

selectText:function(){

    var range,
        selection,
        obj = this[0],
        type = {
            func:'function',
            obj:'object'
        },
        // Convenience
        is = function(type, o){
            return typeof o === type;
        };

    if(is(type.obj, obj.ownerDocument)
        && is(type.obj, obj.ownerDocument.defaultView)
        && is(type.func, obj.ownerDocument.defaultView.getSelection)){

        selection = obj.ownerDocument.defaultView.getSelection();

        if(is(type.func, selection.setBaseAndExtent)){
            // Chrome, Safari - nice and easy
            selection.setBaseAndExtent(obj, 0, obj, $(obj).contents().size());
        }
        else if(is(type.func, obj.ownerDocument.createRange)){

            range = obj.ownerDocument.createRange();

            if(is(type.func, range.selectNodeContents)
                && is(type.func, selection.removeAllRanges)
                && is(type.func, selection.addRange)){
                // Mozilla
                range.selectNodeContents(obj);
                selection.removeAllRanges();
                selection.addRange(range);
            }
        }
    }
    else if(is(type.obj, document.body) && is(type.obj, document.body.createTextRange)) {

        range = document.body.createTextRange();

        if(is(type.obj, range.moveToElementText) && is(type.obj, range.select)){
            // IE most likely
            range.moveToElementText(obj);
            range.select();
        }
    }

    // Chainable
    return this;
}

C'est ça. Une partie de ce que vous voyez est la lisibilité et / ou la commodité. Testé sur Mac dans les dernières versions d'Opera, Safari, Chrome, Firefox et IE. Également testé dans IE8. De plus, je ne déclare généralement les variables que si / quand cela est nécessaire dans les blocs de code, mais jslint a suggéré qu'elles soient toutes déclarées en haut. Ok jslint.

Modifier J'ai oublié d'inclure comment lier cela au code de l'op:

function SelectText(element) {
    $("#" + element).selectText();
}

À votre santé

Madbreaks
la source
Oui, cela me semble brutal, bien que cela semble correct. Mon principal reproche est que l'utilisation du non standard setBaseAndExtent()simplement parce qu'il existe me semble inutile lorsque vous pouvez simplement supprimer cette branche et que tout fonctionne aussi bien et de manière normalisée. La détection des fonctionnalités est agréable mais je serais fatigué de tester tout cela assez rapidement.
Tim Down
Eh bien @TimDown, le point de levier setBaseAndExtentest que c'est beaucoup plus efficace, et même avec le ifstatut ajouté, c'est encore beaucoup plus que si vous "supprimez cette branche". Je ne comprends pas vraiment le commentaire de "je serais fatigué .."? Écrivez-le et oubliez-le, la seule chose que vous avez à faire est d'appeler la fonction, pas de l'écrire. :)
Madbreaks
@Madbreaks Je serais surpris si setBaseAndExtentc'était beaucoup plus performant que addRange. Pourquoi serait-ce? Mon autre commentaire était lié à votre philosophie de test de fonctionnalités étendue à toutes les interactions DOM: c'est énormément de code pour tester chaque méthode et propriété DOM avant de l'utiliser. Je ne désapprouve pas; Je suis juste heureux de tracer la ligne et de faire quelques hypothèses de plus dans mon code.
Tim Down du
4

Une version mise à jour qui fonctionne en chrome:

function SelectText(element) {
    var doc = document;
    var text = doc.getElementById(element);    
    if (doc.body.createTextRange) { // ms
        var range = doc.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if (window.getSelection) {
        var selection = window.getSelection();
        var range = doc.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);

    }
}

$(function() {
    $('p').click(function() {
        SelectText("selectme");

    });
});

http://jsfiddle.net/KcX6A/326/

Kimtho6
la source
3

Pour n'importe quelle balise, on peut sélectionner tout le texte à l'intérieur de cette balise par ce code court et simple. Il mettra en évidence toute la zone de balise avec une couleur jaune et sélectionnera le texte à l'intérieur en un seul clic.

document.onclick = function(event) {
    var range, selection;
event.target.style.backgroundColor = 'yellow';
        selection = window.getSelection();
        range = document.createRange();
        range.selectNodeContents(event.target);
        selection.removeAllRanges();
        selection.addRange(range);
};
knjhaveri
la source
2

lepe - Cela fonctionne très bien pour moi merci! J'ai mis votre code dans un fichier de plugin, puis je l'ai utilisé en conjonction avec chaque déclaration afin que vous puissiez avoir plusieurs pré balises et plusieurs liens "Sélectionner tout" sur une seule page et il choisit le bon pré pour mettre en évidence:

<script type="text/javascript" src="../js/jquery.selecttext.js"></script>
<script type="text/javascript">
  $(document).ready(function() { 
        $(".selectText").each(function(indx) {
                $(this).click(function() {                 
                    $('pre').eq(indx).selText().addClass("selected");
                        return false;               
                    });
        });
  });

DaveR
la source
1

Jetez un oeil à l' objet Selection (moteur Gecko) et à l' objet TextRange (moteur Trident.) Je ne connais aucun framework JavaScript prenant en charge plusieurs navigateurs pour cela, mais je ne l'ai jamais cherché non plus, donc il est possible que même jQuery l'ait.

Blixt
la source
0

La méthode de Tim fonctionne parfaitement pour mon cas - en sélectionnant le texte dans un div pour IE et FF après avoir remplacé la déclaration suivante:

range.moveToElementText(text);

avec ce qui suit:

range.moveToElementText(el);

Le texte dans le div est sélectionné en cliquant dessus avec la fonction jQuery suivante:

$(function () {
    $("#divFoo").click(function () {
        selectElementText(document.getElementById("divFoo"));
    })
});
Hong
la source
0

voici une autre solution simple pour obtenir le texte sélectionné sous forme de chaîne, vous pouvez facilement utiliser cette chaîne pour ajouter un enfant élément div dans votre code:

var text = '';

if (window.getSelection) {
    text = window.getSelection();

} else if (document.getSelection) {
    text = document.getSelection();

} else if (document.selection) {
    text = document.selection.createRange().text;
}

text = text.toString();
Nadeem Yasin
la source
C'est pour retourner le texte en surbrillance, pas pour créer une surbrillance.
MKN Web Solutions
0

Mon cas d'utilisation particulier consistait à sélectionner une plage de texte à l'intérieur d'un élément span modifiable, qui, à ma connaissance, n'est décrit dans aucune des réponses ici.

La principale différence est que vous devez passer un nœud de type Textà l' Rangeobjet, comme décrit dans la documentation de Range.setStart () :

Si startNode est un Node de type Text, Comment ou CDATASection , 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.

Le Textnœud est le premier nœud enfant d'un élément span, donc pour l'obtenir, l'accès à childNodes[0]l'élément span. Le reste est le même que dans la plupart des autres réponses.

Voici un exemple de code:

var startIndex = 1;
var endIndex = 5;
var element = document.getElementById("spanId");
var textNode = element.childNodes[0];

var range = document.createRange();
range.setStart(textNode, startIndex);
range.setEnd(textNode, endIndex);

var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);

Autre documentation pertinente:
Range
Selection
Document.createRange ()
Window.getSelection ()

Domysee
la source
-1

Ajouté jQuery.browser.webkitau "else if" pour Chrome. Impossible de faire fonctionner cela dans Chrome 23.

Faites ce script ci-dessous pour sélectionner le contenu dans une <pre>balise qui a le class="code".

jQuery( document ).ready(function() {
    jQuery('pre.code').attr('title', 'Click to select all');
    jQuery( '#divFoo' ).click( function() {
        var refNode = jQuery( this )[0];
        if ( jQuery.browser.msie ) {
            var range = document.body.createTextRange();
            range.moveToElementText( refNode );
            range.select();
        } else if ( jQuery.browser.mozilla || jQuery.browser.opera  || jQuery.browser.webkit ) {
            var selection = refNode.ownerDocument.defaultView.getSelection();
            console.log(selection);
            var range = refNode.ownerDocument.createRange();
            range.selectNodeContents( refNode );
            selection.removeAllRanges();
            selection.addRange( range );
        } else if ( jQuery.browser.safari ) {
            var selection = refNode.ownerDocument.defaultView.getSelection();
            selection.setBaseAndExtent( refNode, 0, refNode, 1 );
        }
    } );
} );
Derk
la source
-1

Selon la documentation jQuery de select():

Déclenchez l'événement select de chaque élément correspondant. Cela provoque l'exécution de toutes les fonctions liées à cet événement select et appelle l'action de sélection par défaut du navigateur sur le ou les éléments correspondants.

Il y a votre explication pourquoi jQuery select()ne fonctionnera pas dans ce cas.

Kees de Kooter
la source
4
je n'essaye pas de mettre en évidence le texte avec un style css. je veux que le texte soit sélectionné.
Jason