選択したテキストをdivコンテナーでスパンでラップしたいのですが、可能ですか?
ユーザーがテキストを選択してボタンをクリックすると、ボタンクリックイベントで、選択したテキストをスパン要素でラップします。 window.getSelection()
を使用して選択したテキストを取得できますが、DOM構造内の正確な位置を知る方法は?
選択範囲が1つのテキストノードに完全に含まれている場合は、選択範囲から取得する範囲の surroundContents()
メソッドを使用してこれを行うことができます。ただし、これは非常に脆弱です。選択範囲を論理的に単一の要素で囲むことができない場合は機能しません(通常、範囲がノードの境界を超えている場合、これは 正確な定義 ではありません)。一般的なケースでこれを行うには、より複雑なアプローチが必要です。
また、DOM Range
とwindow.getSelection()
は、IE <9.ではサポートされていません。これらのブラウザーには、別のアプローチが再び必要になります。ライブラリを使用できます。私自身のような Rangy はブラウザの動作を正規化します class applier module がこの質問に役立つかもしれません。
単純なsurroundContents()
の例jsFiddle: http://jsfiddle.net/VRcvn/
コード:
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);
}
}
}
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フィドル 。
可能です。範囲APIとRange.surroundContents()メソッドを使用する必要があります。コンテンツがラップされるノードを指定された範囲の先頭に配置します。 https://developer.mozilla.org/en/DOM/range.surroundContents を参照してください
surroundContentsは、選択にテキストのみが含まれ、HTMLが含まれていない場合にのみ機能します。これは、より柔軟なクロスブラウザソリューションです。これは次のようなスパンを挿入します:
<span id="new_selection_span"><!--MARK--></span>
スパンは選択範囲の前、最も近い開始HTMLタグの前に挿入されます。
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();
}
以下のコードは、あらゆる種類のタグのspanタグをラップするのに役立ちます。コードを確認し、実装のロジックを使用してください。
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';
}
}
}
}
}
}