Related
I would like to have users click a link, then it selects the HTML text in another element (not an input).
By "select" I mean the same way you would select text by dragging your mouse over it. This has been a bear to research because everyone talks about "select" or "highlight" in other terms.
Is this possible? My code so far:
HTML:
Select Code
<code id="xhtml-code">Some Code here </code>
JS:
function SelectText(element) {
$("#" + element).select();
}
Am I missing something blatantly obvious?
Plain Javascript
function selectText(nodeId) {
const node = document.getElementById(nodeId);
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>
Here is a working demo. For those of you looking for a jQuery plugin, I made one of those too.
jQuery (original answer)
I have found a solution for this in this thread. I was able to modify the info given and mix it with a bit of jQuery to create a totally awesome function to select the text in any element, regardless of browser:
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);
}
}
Here's a version with no browser sniffing and no reliance on 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);
This thread (dead link) contains really wonderful stuff. But I'm not able to do it right on this page using FF 3.5b99 + FireBug due to "Security Error".
Yipee!! I was able to select whole right hand sidebar with this code hope it helps you:
var r = document.createRange();
var w=document.getElementById("sidebar");
r.selectNodeContents(w);
var sel=window.getSelection();
sel.removeAllRanges();
sel.addRange(r);
PS:- I was not able to use objects returned by jquery selectors like
var w=$("div.welovestackoverflow",$("div.sidebar"));
//this throws **security exception**
r.selectNodeContents(w);
Jason's code can not be used for elements inside an iframe (as the scope differs from window and document). I fixed that problem and I modified it in order to be used as any other jQuery plugin (chainable):
Example 1: Selection of all text inside < code > tags with single click and add class "selected":
$(function() {
$("code").click(function() {
$(this).selText().addClass("selected");
});
});
Example 2: On button click, select an element inside an Iframe:
$(function() {
$("button").click(function() {
$("iframe").contents().find("#selectme").selText();
});
});
Note: remember that the iframe source should reside in the same domain to prevent security errors.
jQuery Plugin:
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;
}
I tested it in IE8, Firefox, Opera, Safari, Chrome (current versions). I'm not sure if it works in older IE versions (sincerely I don't care).
You can use the following function to select content of any element:
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);
}
};
This function can be called as follows:
$('#selectme').selectText();
I was searching for the same thing, my solution was this:
$('#el-id').focus().select();
I liked lepe's answer except for a few things:
Browser-sniffing, jQuery or no isn't optimal
DRY
Doesn't work in IE8 if obj's parent doesn't support createTextRange
Chrome's ability to use setBaseAndExtent should be leveraged (IMO)
Will not select text spanning across multiple DOM elements (elements within the "selected" element). In other words if you call selText on a div containing multiple span elements, it will not select the text of each of those elements. That was a deal-breaker for me, YMMV.
Here's what I came up with, with a nod to lepe's answer for inspiration. I'm sure I'll be ridiculed as this is perhaps a bit heavy-handed (and actually could be moreso but I digress). But it works and avoids browser-sniffing and that's the 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;
}
That's it. Some of what you see is the for readability and/or convenience. Tested on Mac in latest versions of Opera, Safari, Chrome, Firefox and IE. Also tested in IE8. Also I typically only declare variables if/when needed inside code blocks but jslint suggested they all be declared up top. Ok jslint.
Edit
I forgot to include how to tie this in to the op's code:
function SelectText(element) {
$("#" + element).selectText();
}
Cheers
An Updated version that works in 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/
For any tag one can select all text inside that tag by this short and simple code. It will highlight the entire tag area with yellow colour and select text inside it on single click.
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);
};
Have a look at the Selection object (Gecko engine) and the TextRange object (Trident engine.) I don't know about any JavaScript frameworks that have cross-browser support for this implemented, but I've never looked for it either, so it's possible that even jQuery has it.
lepe - That works great for me thanks!
I put your code in a plugin file, then used it in conjunction with an each statement so you can have multiple pre tags and multiple "Select all" links on one page and it picks out the correct pre to highlight:
<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;
});
});
});
Tim's method works perfectly for my case - selecting the text in a div for both IE and FF after I replaced the following statement:
range.moveToElementText(text);
with the following:
range.moveToElementText(el);
The text in the div is selected by clicking it with the following jQuery function:
$(function () {
$("#divFoo").click(function () {
selectElementText(document.getElementById("divFoo"));
})
});
here is another simple solution to get the selected the text in the form of string, you can use this string easily to append a div element child into your 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();
My particular use-case was selecting a text range inside an editable span element, which, as far as I could see, is not described in any of the answers here.
The main difference is that you have to pass a node of type Text to the Range object, as described in the documentation of Range.setStart():
If the startNode is a Node of type Text, Comment, or CDATASection,
then startOffset is the number of characters from the start of
startNode. For other Node types, startOffset is the number of child
nodes between the start of the startNode.
The Text node is the first child node of a span element, so to get it, access childNodes[0] of the span element. The rest is the same as in most other answers.
Here a code example:
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);
Other relevant documentation:
Range
Selection
Document.createRange()
Window.getSelection()
Added jQuery.browser.webkit to the "else if" for Chrome. Could not get this working in Chrome 23.
Made this script below for selecting the content in a <pre> tag that has the 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 );
}
} );
} );
According to the jQuery documentation of select():
Trigger the select event of each matched element. This causes all of the functions that have been bound to that select event to be executed, and calls the browser's default select action on the matching element(s).
There is your explanation why the jQuery select() won't work in this case.
I have a contentEditable div where I want to insert HTML tags (a simple span element).
Is there a cross browser solution that allows me to insert those tags over my div selection or cursor position. If something else is selected on the page (not in the div), I want to append the tag to the end of the div.
Thanks
Here is a kickstart
// get the selection range (or cursor position)
var range = window.getSelection().getRangeAt(0);
// create a span
var newElement = document.createElement('span');
newElement.id = 'myId';
newElement.innerHTML = 'Hello World!';
// if the range is in #myDiv ;)
if(range.startContainer.parentNode.id==='myDiv') {
// delete whatever is on the range
range.deleteContents();
// place your span
range.insertNode(newElement);
}
I don't have IE but works fine on firefox, chrome and safari. Maybe you want to play with range.startContainer to proceed only if the selection is made on the contentEditable div.
EDIT: According to quirksmode range intro you have to change the window.getSelection() part to be IE compatible.
var userSelection;
if (window.getSelection) {
userSelection = window.getSelection();
}
else if (document.selection) { // should come last; Opera!
userSelection = document.selection.createRange();
}
The following will do this in all major browsers (including IE 6). It will also handle cases where the end of the selection is outside your <div> and cases where the selection is contained within a child (or more deeply nested) element inside the <div>.
2019 addendum: The second branch of insertNodeOverSelection is for IE <= 8 only and could be removed now.
function isOrContainsNode(ancestor, descendant) {
var node = descendant;
while (node) {
if (node === ancestor) return true;
node = node.parentNode;
}
return false;
}
function insertNodeOverSelection(node, containerNode) {
var sel, range, html;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
if (isOrContainsNode(containerNode, range.commonAncestorContainer)) {
range.deleteContents();
range.insertNode(node);
} else {
containerNode.appendChild(node);
}
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
if (isOrContainsNode(containerNode, range.parentElement())) {
html = (node.nodeType == 3) ? node.data : node.outerHTML;
range.pasteHTML(html);
} else {
containerNode.appendChild(node);
}
}
}
<input type="button" onmousedown="insertNodeOverSelection(document.createTextNode('[NODE]'), document.getElementById('test'));" value="insert">
<div contenteditable="true">
<div id="test" style="background-color: lightgoldenrodyellow">
This is the editable element where the insertion will happen. Select something or place the cursor in here, then hit the button above
</div>
<div>
No insertion will happen here
</div>
</div>
This function uses the range object to return user selection and wrap it in bold tags. is there a method that allows me to remove the tags? As in <b>text<b> = text.
I actually need a toggle function that wraps the selection in tags & un-wraps it if already contains tags. Similar to what text editors do when you toggle the bold button.
if "text" then "<b>text</b>"
else "<b>text</b>" then "text"
...
function makeBold() {
//create variable from selection
var selection = window.getSelection();
if (selection.rangeCount) {
var range = selection.getRangeAt(0).cloneRange();
var newNode = document.createElement("b");
//wrap selection in tags
range.surroundContents(newNode);
//return the user selection
selection.removeAllRanges();
selection.addRange(range);
}
}
I didn't mention this in your previous question about this because it sounded like you wanted a generic means of surrounding a range within an element, but for this particular application (i.e. bolding/unbolding text), and assuming you don't mind a little cross-browser variation in the precise tags used (<strong> versus <bold> versus possibly <span style="font-weight: bold">), you're best off using document.execCommand(), which will toggle boldness:
function toggleBold() {
document.execCommand("bold", false, null);
}
This will work in all browsers when the selected content is editable, and even when it's not editable in IE. If you need it to work on non-editable content in other browsers, you'll need to temporarily make the document editable:
function toggleBold() {
var range, sel;
if (window.getSelection) {
// Non-IE case
sel = window.getSelection();
if (sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
document.execCommand("bold", false, null);
document.designMode = "off";
} else if (document.selection && document.selection.createRange &&
document.selection.type != "None") {
// IE case
range = document.selection.createRange();
range.execCommand("bold", false, null);
}
}
wrapping & un-wrapping the text selection using the same button click event handler:
boldBtn.addEventListener('click', () => {
let selection = document.getSelection();
const isAllowedContainer = selection.baseNode.parentElement?.closest?.('#editor');
// do not continue if no text selection or this is not the desired element container
if( selection.rangeCount < 1 || !isAllowedContainer ) return;
const range = selection.getRangeAt(0);
const selParent = selection.anchorNode?.parentElement;
const selectedElem = selParent?.nodeType == 1 && selParent?.children.length < 2 && selParent;
// un-wrap
if(selectedElem.tagName === 'B') {
selectedElem.replaceWith(...selectedElem.childNodes)
}
// wrap with <b>
else {
range.surroundContents(document.createElement("b"));
selection.removeAllRanges();
selection.addRange(range);
range.collapse(); // removes selected and places caret at the end of the injected node
}
})
<button id="boldBtn">B</button><br/><br/>
<div id='editor' contenteditable="true">Select some text and click the button</div>
I want to be able to double-click to select some text in a div, which then triggers a JavaScript function that inserts some HTML after the selected text, much like the 'edit/reply' feature in Google Wave.
I have already established how to trigger a function on double-clicking, it is locating the selection and subsequently inserting HTML after it that is the problem.
The following function will insert a DOM node (element or text node) at the end of the selection in all major browsers (note that Firefox now allows multiple selections by default; the following uses only the first selection):
function insertNodeAfterSelection(node) {
var sel, range, html;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.collapse(false);
range.insertNode(node);
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.collapse(false);
html = (node.nodeType == 3) ? node.data : node.outerHTML;
range.pasteHTML(html);
}
}
If you would rather insert an HTML string:
function insertHtmlAfterSelection(html) {
var sel, range, node;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = window.getSelection().getRangeAt(0);
range.collapse(false);
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = html;
var frag = document.createDocumentFragment(), node, lastNode;
while ( (node = el.firstChild) ) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.collapse(false);
range.pasteHTML(html);
}
}
Update 18 January 2012
Finally, here's a version that inserts HTML and preserves the selection (i.e. expands the selection to include the originally selected content plus the inserted content).
Live demo: http://jsfiddle.net/timdown/JPb75/1/
Code:
function insertHtmlAfterSelection(html) {
var sel, range, expandedSelRange, node;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = window.getSelection().getRangeAt(0);
expandedSelRange = range.cloneRange();
range.collapse(false);
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = html;
var frag = document.createDocumentFragment(), node, lastNode;
while ( (node = el.firstChild) ) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
expandedSelRange.setEndAfter(lastNode);
sel.removeAllRanges();
sel.addRange(expandedSelRange);
}
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
expandedSelRange = range.duplicate();
range.collapse(false);
range.pasteHTML(html);
expandedSelRange.setEndPoint("EndToEnd", range);
expandedSelRange.select();
}
}
window.getSelection() will get you the selected text (Documentation).
You can use the jQuery.after() (Documentation) to insert new html after an element.
On your selected text function call you will have to loop through all your DOM elements checking their x&y positions to the current cursor position - get the closest element and insert after or use the function .elementFromPoint(x,y) (Documentation)
This is the best way I can currently think of. It's not perfect, but it is somewhere to start.
I don't know if it is possible, but I though of a partial solution.
On your double click event you can get the event object and access the property target.
Having the target element, you can get the selected text.
Now get the target element html and find the index of the selected text and it's length, inject your html on the index + length position.
I think you already saw the problem, it could match the text in multiple places. To solve it you might have to find the position of the cursor and compare to the element, I don't know how to do that...
Hope I could help a bit.
I would like to have users click a link, then it selects the HTML text in another element (not an input).
By "select" I mean the same way you would select text by dragging your mouse over it. This has been a bear to research because everyone talks about "select" or "highlight" in other terms.
Is this possible? My code so far:
HTML:
Select Code
<code id="xhtml-code">Some Code here </code>
JS:
function SelectText(element) {
$("#" + element).select();
}
Am I missing something blatantly obvious?
Plain Javascript
function selectText(nodeId) {
const node = document.getElementById(nodeId);
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>
Here is a working demo. For those of you looking for a jQuery plugin, I made one of those too.
jQuery (original answer)
I have found a solution for this in this thread. I was able to modify the info given and mix it with a bit of jQuery to create a totally awesome function to select the text in any element, regardless of browser:
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);
}
}
Here's a version with no browser sniffing and no reliance on 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);
This thread (dead link) contains really wonderful stuff. But I'm not able to do it right on this page using FF 3.5b99 + FireBug due to "Security Error".
Yipee!! I was able to select whole right hand sidebar with this code hope it helps you:
var r = document.createRange();
var w=document.getElementById("sidebar");
r.selectNodeContents(w);
var sel=window.getSelection();
sel.removeAllRanges();
sel.addRange(r);
PS:- I was not able to use objects returned by jquery selectors like
var w=$("div.welovestackoverflow",$("div.sidebar"));
//this throws **security exception**
r.selectNodeContents(w);
Jason's code can not be used for elements inside an iframe (as the scope differs from window and document). I fixed that problem and I modified it in order to be used as any other jQuery plugin (chainable):
Example 1: Selection of all text inside < code > tags with single click and add class "selected":
$(function() {
$("code").click(function() {
$(this).selText().addClass("selected");
});
});
Example 2: On button click, select an element inside an Iframe:
$(function() {
$("button").click(function() {
$("iframe").contents().find("#selectme").selText();
});
});
Note: remember that the iframe source should reside in the same domain to prevent security errors.
jQuery Plugin:
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;
}
I tested it in IE8, Firefox, Opera, Safari, Chrome (current versions). I'm not sure if it works in older IE versions (sincerely I don't care).
You can use the following function to select content of any element:
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);
}
};
This function can be called as follows:
$('#selectme').selectText();
I was searching for the same thing, my solution was this:
$('#el-id').focus().select();
I liked lepe's answer except for a few things:
Browser-sniffing, jQuery or no isn't optimal
DRY
Doesn't work in IE8 if obj's parent doesn't support createTextRange
Chrome's ability to use setBaseAndExtent should be leveraged (IMO)
Will not select text spanning across multiple DOM elements (elements within the "selected" element). In other words if you call selText on a div containing multiple span elements, it will not select the text of each of those elements. That was a deal-breaker for me, YMMV.
Here's what I came up with, with a nod to lepe's answer for inspiration. I'm sure I'll be ridiculed as this is perhaps a bit heavy-handed (and actually could be moreso but I digress). But it works and avoids browser-sniffing and that's the 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;
}
That's it. Some of what you see is the for readability and/or convenience. Tested on Mac in latest versions of Opera, Safari, Chrome, Firefox and IE. Also tested in IE8. Also I typically only declare variables if/when needed inside code blocks but jslint suggested they all be declared up top. Ok jslint.
Edit
I forgot to include how to tie this in to the op's code:
function SelectText(element) {
$("#" + element).selectText();
}
Cheers
An Updated version that works in 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/
For any tag one can select all text inside that tag by this short and simple code. It will highlight the entire tag area with yellow colour and select text inside it on single click.
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);
};
Have a look at the Selection object (Gecko engine) and the TextRange object (Trident engine.) I don't know about any JavaScript frameworks that have cross-browser support for this implemented, but I've never looked for it either, so it's possible that even jQuery has it.
lepe - That works great for me thanks!
I put your code in a plugin file, then used it in conjunction with an each statement so you can have multiple pre tags and multiple "Select all" links on one page and it picks out the correct pre to highlight:
<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;
});
});
});
Tim's method works perfectly for my case - selecting the text in a div for both IE and FF after I replaced the following statement:
range.moveToElementText(text);
with the following:
range.moveToElementText(el);
The text in the div is selected by clicking it with the following jQuery function:
$(function () {
$("#divFoo").click(function () {
selectElementText(document.getElementById("divFoo"));
})
});
here is another simple solution to get the selected the text in the form of string, you can use this string easily to append a div element child into your 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();
My particular use-case was selecting a text range inside an editable span element, which, as far as I could see, is not described in any of the answers here.
The main difference is that you have to pass a node of type Text to the Range object, as described in the documentation of Range.setStart():
If the startNode is a Node of type Text, Comment, or CDATASection,
then startOffset is the number of characters from the start of
startNode. For other Node types, startOffset is the number of child
nodes between the start of the startNode.
The Text node is the first child node of a span element, so to get it, access childNodes[0] of the span element. The rest is the same as in most other answers.
Here a code example:
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);
Other relevant documentation:
Range
Selection
Document.createRange()
Window.getSelection()
Added jQuery.browser.webkit to the "else if" for Chrome. Could not get this working in Chrome 23.
Made this script below for selecting the content in a <pre> tag that has the 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 );
}
} );
} );
According to the jQuery documentation of select():
Trigger the select event of each matched element. This causes all of the functions that have been bound to that select event to be executed, and calls the browser's default select action on the matching element(s).
There is your explanation why the jQuery select() won't work in this case.