web-dev-qa-db-fra.com

Envelopper un noeud de texte sélectionné avec span

Je veux envelopper un texte sélectionné dans un conteneur div avec span, est-ce possible?

Un utilisateur sélectionnera un texte et cliquera sur un bouton. L'événement de clic de bouton que je souhaite envelopper le texte sélectionné avec un élément span Je peux obtenir le texte sélectionné en utilisant window.getSelection() mais comment connaître sa position exacte dans la structure du DOM?

35
coure2011

Si la sélection est entièrement contenue dans un seul nœud de texte, vous pouvez le faire à l'aide de la méthode surroundContents() de la plage obtenue à partir de la sélection. Cependant, ceci est très fragile: cela ne fonctionne pas si la sélection ne peut pas être logiquement entourée d'un seul élément (généralement, si la plage dépasse les limites des nœuds, bien que ce ne soit pas la définition precise ). Pour ce faire dans le cas général, vous avez besoin d’une approche plus compliquée.

De plus, les DOM Range et window.getSelection() ne sont pas pris en charge dans IE <9. Vous aurez besoin d'une autre approche pour ces navigateurs. Vous pouvez utiliser une bibliothèque telle que mon propre Rangy pour normaliser le comportement du navigateur. Le module applicateur class peut être utile pour cette question.

Exemple simple surroundContents() jsFiddle: http://jsfiddle.net/VRcvn/

Code:

function surroundSelection(element) {
    if (window.getSelection) {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var range = sel.getRangeAt(0).cloneRange();
            range.surroundContents(element);
            sel.removeAllRanges();
            sel.addRange(range);
        }
    }
}
52
Tim Down

function wrapSelectedText() {       
    var selection= window.getSelection().getRangeAt(0);
    var selectedText = selection.extractContents();
    var span= document.createElement("span");
    span.style.backgroundColor = "yellow";
    span.appendChild(selectedText);
    selection.insertNode(span);
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus  gravida magna, quis interdum magna mattis quis. Fusce tempor sagittis  varius. Nunc at augue at erat suscipit bibendum id nec enim. Sed eu odio  quis turpis hendrerit sagittis id sit amet justo. Cras ac urna purus,  non rutrum nunc. Aenean nec vulputate ante. Morbi scelerisque sagittis  hendrerit. Pellentesque habitant morbi tristique senectus et netus et  malesuada fames ac turpis egestas. Nulla tristique ligula fermentum  tortor semper at consectetur erat aliquam. Sed gravida consectetur  sollicitudin. 

<input type="button" onclick="wrapSelectedText();" value="Highlight" />

JS Fiddle .

18
amal

c'est possible. Vous devez utiliser l'API de plage et la méthode Range.surroundContents (). Il place le nœud dans lequel le contenu est encapsulé au début de la plage spécifiée . Voir https://developer.mozilla.org/en/DOM/range.surroundContents

4
JanD

surroundContents ne fonctionne que si votre sélection ne contient que du texte et pas de HTML. Voici une solution plus flexible, ainsi que multi-navigateur. Cela va insérer une étendue comme ceci:

<span id="new_selection_span"><!--MARK--></span>

La plage est insérée avant la sélection, devant la balise HTML d'ouverture la plus proche.

var span = document.createElement("span");
span.id = "new_selection_span";
span.innerHTML = '<!--MARK-->';

if (window.getSelection) { //compliant browsers
    //obtain the selection
    sel = window.getSelection();
    if (sel.rangeCount) {
        //clone the Range object
        var range = sel.getRangeAt(0).cloneRange();
        //get the node at the start of the range
        var node = range.startContainer;
        //find the first parent that is a real HTML tag and not a text node
        while (node.nodeType != 1) node = node.parentNode;
        //place the marker before the node
        node.parentNode.insertBefore(span, node);
        //restore the selection
        sel.removeAllRanges();
        sel.addRange(range);
    }
} else { //IE8 and lower
    sel = document.selection.createRange();
    //place the marker before the node
    var node = sel.parentElement();
    node.parentNode.insertBefore(span, node);
    //restore the selection
    sel.select();
}
3
Ali Gangji

S'il vous plaît trouver le code ci-dessous sera utile pour envelopper la balise span pour tout type de balises. Veuillez parcourir le code et utiliser la logique pour votre implémentation.

getSelectedText(this);
addAnnotationElement(this, this.parent);

function getSelectedText(this) {
    this.range = window.getSelection().getRangeAt(0);
    this.parent = this.range.commonAncestorContainer;
    this.frag = this.range.cloneContents();
    this.clRange = this.range.cloneRange();
    this.start = this.range.startContainer;
    this.end = this.range.endContainer;
}


function addAnnotationElement(this, elem) {
    var text, textParent, origText, prevText, nextText, childCount,
        annotationTextRange,
        span = this.htmlDoc.createElement('span');

    if (elem.nodeType === 3) {
        span.setAttribute('class', this.annotationClass);
        span.dataset.name = this.annotationName;
        span.dataset.comment = '';
        span.dataset.page = '1';
        origText = elem.textContent;            
        annotationTextRange = validateTextRange(this, elem);
        if (annotationTextRange == 'textBeforeRangeButIntersect') {
            text = origText.substring(0, this.range.endOffset);
            nextText = origText.substring(this.range.endOffset);
        } else if (annotationTextRange == 'textAfterRangeButIntersect') {
            prevText = origText.substring(0, this.range.startOffset);
            text = origText.substring(this.range.startOffset);
        } else if (annotationTextRange == 'textExactlyInRange') {
            text = origText
        } else if (annotationTextRange == 'textWithinRange') {
            prevText = origText.substring(0, this.range.startOffset);
            text = origText.substring(this.range.startOffset,this.range.endOffset);
            nextText = origText.substring(this.range.endOffset);
        } else if (annotationTextRange == 'textNotInRange') {
            return;
        }
        span.textContent = text;
        textParent = elem.parentElement;
        textParent.replaceChild(span, elem);
        if (prevText) {
            var prevDOM = this.htmlDoc.createTextNode(prevText);
            textParent.insertBefore(prevDOM, span);
        }
        if (nextText) {
            var nextDOM = this.htmlDoc.createTextNode(nextText);
            textParent.insertBefore(nextDOM, span.nextSibling);
        }
        return;
    }
    childCount = elem.childNodes.length;
    for (var i = 0; i < childCount; i++) {
        var elemChildNode = elem.childNodes[i];
        if( Helper.isUndefined(elemChildNode.tagName) ||
            ! ( elemChildNode.tagName.toLowerCase() === 'span' &&
            elemChildNode.classList.contains(this.annotationClass) ) ) {
            addAnnotationElement(this, elem.childNodes[i]);
        }
        childCount = elem.childNodes.length;
    }
}

  function validateTextRange(this, elem) {
    var textRange = document.createRange();

    textRange.selectNodeContents (elem);
    if (this.range.compareBoundaryPoints (Range.START_TO_END, textRange) <= 0) {
        return 'textNotInRange';
    }
    else {
        if (this.range.compareBoundaryPoints (Range.END_TO_START, textRange) >= 0) {
            return 'textNotInRange';
        }
        else {
            var startPoints = this.range.compareBoundaryPoints (Range.START_TO_START, textRange),
                endPoints = this.range.compareBoundaryPoints (Range.END_TO_END, textRange);

            if (startPoints < 0) {
                if (endPoints < 0) {
                    return 'textBeforeRangeButIntersect';
                }
                else {
                    return "textExactlyInRange";
                }
            }
            else {
                if (endPoints > 0) {
                    return 'textAfterRangeButIntersect';
                }
                else {
                    if (startPoints === 0 && endPoints === 0) {
                        return "textExactlyInRange";
                    }
                    else {
                        return 'textWithinRange';
                    }
                }
            }
        }
    }
}
0
sathiya