...

View Full Version : get user selected HTML text



Joeweitzel
12-24-2008, 05:51 AM
On IE you can use selection.createRange().htmlText to get the user selected HTML text. Other browsers like Safari and Firefox only have getSelection() which can only return unformated plain text, and there is nothing for HTML text.

Does anyone have a method created to get user selected html text on all browsers?

Thanks

Sephr
12-24-2008, 06:48 AM
Use this:

function getSel() {
if (window.getSelection) {
return window.getSelection();
} else if (document.selection) {
return document.selection.createRange();
}
}

Joeweitzel
12-24-2008, 07:21 AM
getSelection only returns plain text not HTML text. I need HTML text. That's my challenge.

itsallkizza
12-24-2008, 07:57 AM
This works in IE:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Example</title>
<style type="text/css">
</style>
<script type="text/javascript">
// <![CDATA[

function getSelectionHTML()
{
if (document.selection && document.selection.createRange) return (document.selection.createRange()).htmlText;
return null;
}

function doSomething()
{
alert(getSelectionHTML());
}

// ]]>
</script>
</head>
<body>

<div>blah blah <em>blah</em> blah <strong>blah</strong> blah</div>

<input type="button" value="click me" onclick="doSomething()" />

</body>
</html>


I'll brainstorm for a cross-browser version.

Joeweitzel
12-24-2008, 08:38 AM
There is no built-in methods for this on non-IE browsers. What has to be done is a a custom method of getting the start and end position of the getSelected text and then return the innerHTML in a substring. That should do it :)


Thanks!

itsallkizza
12-24-2008, 08:52 AM
This doesn't work, but I need some sleep. Maybe you can arrive at a solution from some of the methods I used:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Example</title>
<style type="text/css">
</style>
<script type="text/javascript">
// <![CDATA[

function getSelectionHTML()
{
if (window.getSelection && (window.getSelection()).rangeCount == 1)
{
var selection_text = (window.getSelection()).toString();
var range = (window.getSelection()).getRangeAt(0);
var parent_html = range.commonAncestorContainer.innerHTML;
//var b = document.createElement("b");
//b.innerHTML = parent_html;
//var parent_text = b.innerText || b.textContent;
var first_text = range.startContainer.nodeValue.substr(range.startOffset);
var last_text = (range.endContainer.nodeValue).substring(0,range.endOffset);
var start = parent_html.indexOf(first_text);
var end = parent_html.indexOf(last_text,start+1)+last_text.length;
return parent_html.substring(start,end);
}
else if (document.selection && document.selection.createRange) return (document.selection.createRange()).htmlText;
return null;
}

function doSomething()
{
alert(getSelectionHTML());
}

// ]]>
</script>
</head>
<body>

<div>blah blah <em>blah</em> blah <strong>blah</strong> blah</div>

<input type="button" value="click me" onclick="doSomething()" />

</body>
</html>

rnd me
12-24-2008, 09:09 AM
look no further:



function selHTML() {

if (window.ActiveXObject) {
var c = document.selection.createRange();
return c.htmlText;
}

var nNd = document.createElement("p");
var w = getSelection().getRangeAt(0);
w.surroundContents(nNd);
return nNd.innerHTML;
}

itsallkizza
12-24-2008, 02:40 PM
Range.surroundContents - didn't occur to me ;)

Nice job.

Perhaps it may be slightly better to run object detection on the specific functions we're going to use though? Also if there is more than one range selected by the user, the code will only return the first range. Also changed "p" to "span" so it wouldn't interfere with the page layout.


function getSelectionHTML()
{
if (window.getSelection)
{
var sel = window.getSelection();
var html = "";
for (var i=0;i<sel.rangeCount;i++)
{
/*Author: rnd_me*/
var nNd = document.createElement("span");
var w = sel.getRangeAt(i);
w.surroundContents(nNd);
html += nNd.innerHTML;
}
return html;
}
else if (document.selection && document.selection.createRange) return (document.selection.createRange()).htmlText;
return null;
}

This still adds a span every time you run it. Since the OP wanted to return the html of the selected text, it seems like it might be an issue adding html to the html in question... Maybe there's an easy way to remove the spans once we're done with them? .outerHTML = .innerHTML would do the trick if all browsers supported outerHTML (most do not :().

Joeweitzel
12-24-2008, 06:25 PM
Yes the surroundContents was the creativity I was looking for :) Thanks!

I rewrote it to remove the added element, and now its working perfectly (you must pass the HTML text's parent element ):


function getSelectedHTML( element ) {
var sel = "";
if( window.getSelection ) {
sel = window.getSelection();
} else if ( document.getSelection ) {
sel = document.getSelection();
} else if ( document.selection && document.selection.createRange ) {
return ( element.selection.createRange() ).htmlText;
}
if( sel != "" ) {
var html = "";
for ( var i = 0; i < sel.rangeCount; i++ ) {
var nNd = document.createElement("span");
var w = sel.getRangeAt(i);
var prevInnerHTML = element.innerHTML;
w.surroundContents( nNd );
html += nNd.innerHTML;
element.innerHTML = prevInnerHTML;
}
return html;
}
return null;
};


One more issue:

When you select HTML like this (underlined = selected text):

This is some <strong>html text</strong> that has been selected.

it returns "<strong>text</strong> that has been"
when it should be returning "text</strong> that has been"

Any ideas?

itsallkizza
12-24-2008, 07:08 PM
You don't need to pass anything:


function getSelectionHTML()
{
if (document.selection && document.selection.createRange) return (document.selection.createRange()).htmlText;
if (window.getSelection)
{
var sel = window.getSelection();
var html = "";
for (var i=0;i<sel.rangeCount;i++)
{
var d = document.createElement("span");
var r = sel.getRangeAt(i);
var parent_element = r.commonAncestorContainer;
var prev_html = parent_element.innerHTML;
r.surroundContents(d);
html += d.innerHTML;
parent_element.innerHTML = prev_html;
}
return html;
}
return null;
}

Sephr
12-24-2008, 08:10 PM
I don't think you should put the content into a new <p> element using surroundsContents. You should just use extractContents.

Joeweitzel
12-24-2008, 08:32 PM
Thanks, but that doesn't always work. Passing the element is more reliable :)

What about the selection issue I mentioned. Any ideas? It seems to add the opening tag to the returned selected html when you select from before to after a close tag. Any ideas?

Thanks!

itsallkizza
12-24-2008, 09:01 PM
Thanks, but that doesn't always work. Passing the element is more reliable
When wouldn't it work? Having to pass an element greatly devalues this script's worth, so if there's holes in the code I'd like to fix them. There's definitely a way to do it without passing anything.



What about the selection issue I mentioned. Any ideas?
I think most people would want/expect it to function as it is currently functioning (ie including the opening tags). If you want to modify it for your personal needs, all you'd need to do is outcome.substr(outcome.indexOf(">")+1). That'll do exactly what you need.

Joeweitzel
12-25-2008, 07:17 AM
I don't think you should put the content into a new <p> element using surroundsContents. You should just use extractContents.

How did you want to use extraContents to do it?

-Itsallkizza
Thanks for the advise, I'll implement it all and post it once it's debugged..

neverwinter
04-17-2009, 07:20 PM
var range = window.getSelection().getRangeAt(0);

var startContainer = range.startContainer;
var spanNode = startContainer.ownerDocument.createElement("layer");

var docfrag = range.extractContents();
spanNode.appendChild(docfrag);

range.insertNode(spanNode);

alert(spanNode.innerHTML);

you can try this

rnd me
11-20-2012, 12:42 PM
look no further:



function selHTML() {

if (window.ActiveXObject) {
var c = document.selection.createRange();
return c.htmlText;
}

var nNd = document.createElement("p");
var w = getSelection().getRangeAt(0);
w.surroundContents(nNd);
return nNd.innerHTML;
}


i noticed this wasn't working in chrome all the time; specifically if the range start and endpoints have different containers. there's a destructive version that works posted, but i thought i would share my harmless and fast update.
it also absolute-izes the hrefs and src, so your selected html will work anywhere it's pasted.


function selHTML() {

if (window.ActiveXObject) {
var c = document.selection.createRange();
return c.htmlText;
}
var x=document.createElement("div");
x.appendChild(getSelection().getRangeAt(0).cloneContents());
[].slice.call(x.querySelectorAll("[src],[href]"))
.forEach(function(a){a.href=a.href; a.src=a.src;;});
return x.innerHTML;
}



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum