Is it possible to get the parent element of a selected text in the page? For example:
<div class="someparent">
Selection of this text should refer to the 'someparent' class.
<span class="spanparent">If this is selected, the parent should be this span</span>
</div>
Because when getting the selected text, it normally gets it from the window or the document (depending on the browser) but is it that possible to get the parent element of the selected text?
Here's a function that will get you the innermost element that contains the whole of the user selection in all major browsers (except when multiple ranges are selected, which is only supported in Firefox. If this is important, I can expand the example to deal with that case too):
function getSelectionParentElement() {
var parentEl = null, sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
parentEl = sel.getRangeAt(0).commonAncestorContainer;
if (parentEl.nodeType != 1) {
parentEl = parentEl.parentNode;
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
parentEl = sel.createRange().parentElement();
}
return parentEl;
}
I'd suggest to use this
window.getSelection().anchorNode.parentElement
I have tested in safari osx 10.9
#Tim Down's answer works good, to add more useful code for reaching the specific parent's html content:
function getSelectionParentElement() {
var parentEl = null, sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
parentEl = sel.getRangeAt(0).commonAncestorContainer;
if (parentEl.nodeType != 1) {
parentEl = parentEl.parentNode;
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
parentEl = sel.createRange().parentElement();
}
while(true){
// I want to reach upper <span> parent
if(parentEl.nodeName == "SPAN"){
console.log(parentEl);
break;
}
else{
parentEl = parentEl.parentNode;
}
}
}
For example:
function getSelectionParentElement() {
var parentEl = null, sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
parentEl = sel.getRangeAt(0).commonAncestorContainer;
if (parentEl.nodeType != 1) {
parentEl = parentEl.parentNode;
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
parentEl = sel.createRange().parentElement();
}
while(true){
// I want to reach upper <span> parent
if(parentEl.nodeName == "P"){
document.getElementById("printable").innerText = parentEl.innerHTML;
break;
}
else{
parentEl = parentEl.parentNode;
}
}
}
<head>
<style type="text/css">
#media print
{
#non-printable { display: none; }
#printable { display: block; }
}
</style>
</head>
<p>The <strong>coronavirus</strong> COVID-19 is affecting <strong>210 <i>countries</i> and territories</strong> around the world and 2 international conveyances.</p>
<div id="printable">Output: </div>
<button onclick="getSelectionParentElement()">Select 'countries' and click me.</button>
Related
I am using the following function to wrap highlighted text with a <span> when the backspace/delete key is hit.
$(document).keydown(function(event) {
var selection = document.getSelection();
typoNumber++;
if (event.keyCode == 8 || event.keyCode == 46) {
event.preventDefault();
var span = document.createElement("span");
span.setAttribute('id', 'typo' + typoNumber);
span.className = "deleted typo";
span.setAttribute('contenteditable', 'false');
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
});
.deleted.typo {
background: rgba(100,100,100,0.25);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Here is text to experiment with.</div>
I only want the function to run if there is text selected (so that I don't get a bunch of empty <span> elements). I tried changing the first if statement to the following:
if (selection !== '' && event.keyCode == 8 || event.keyCode == 46) {
but that didn't work. Any ideas?
Changing your condition to this should help you:
if(
selection.anchorOffset !== selection.focusOffset
&&
(event.keyCode == 8 || event.keyCode == 46)
)
Fiddle: https://jsfiddle.net/34L49xLr/
getSelection actually returns an object (a SelectionObject) - you can convert that to a string and then check its length to determine whether or not there's any text selected. Something like this:
// Make sure you're initializing this somewhere; this function expects it defined.
var typoNumber = 0;
$(document).keydown(function(event) {
var selection = document.getSelection();
// If there's nothing selected, bail out here.
// If you want to increment typoNumber even with an empty selection,
// move that line above this block.
if (selection.toString().length < 1) {
return;
}
typoNumber++;
if (event.keyCode == 8 || event.keyCode == 46) {
event.preventDefault();
var span = document.createElement("span");
span.setAttribute('id', 'typo' + typoNumber);
span.className = "deleted typo";
span.setAttribute('contenteditable', 'false');
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
});
/* so we can see where the spans are... */
.deleted.typo {
display: inline-block;
padding: 3px;
background: rgba(100,100,100,0.25);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Here is text to experiment with.</div>
Saw this post about getting the html from the selected/higlighted text. And it does work in getting the html.
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
}
alert(getSelectionHtml());
But, suppose there's this html:
<p>I am <span style="color:green;">green</span> and I have <span style="background-color:yellow;">yellow background<span>.</p>
Now if the word green is highlighted, then it will just get the word
"green"
even if there's <span> tag surrounded. But if selected more character than the word green (say the space before or after the word green like this " green"), it will get the html tag too, such as:
<span style="color:green;">green</span>
Please check out the demo
Is it possible to get the html even if only the word "green" is highlighted?
Eventually what I want to achieve is suppose I want to change the color of the highlighted text to blue color, then first check if the highlighted text has a span. Secondly whether that span has color or background or even both styling. And lastly do the changes to the highlighted text.
$('#change_blue').on("click", function() {
var sel = getSelectionHtml();
/*var span = sel.find("<span").html();*/
alert(sel);
if (sel.match("<span style=")) {
console.log('has span tag');
if (sel.indexOf("background") > -1 && sel.indexOf("color") > -1 ) {
console.log('has both background and color')
// change color to blue
}
else if (sel.match("color")) {
console.log('only color')
// change color to blue
}
else {
console.log('only background')
// add blue color
}
}
else {
console.log('no span tag');
}
});
How can I get the html of a highlighted text and change it accordingly? It would really mean a lot if you could help me through. Thank you.
Demo
Do this,
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount, range; i < len; ++i) {
range = sel.getRangeAt(i);
if (range.startContainer === range.endContainer
&& range.startContainer.nodeType === Node.TEXT_NODE
&& range.startOffset === 0
&& range.endOffset === range.startContainer.length) {
range.selectNode(range.startContainer.parentElement);
}
container.appendChild(range.cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
}
I am trying to write a function to add links , so that my users can select a text and attach a link to the text. ( I am trying to imitate how gmail easily lets us to add,delete or edit link to a selected text inside the email)
Here is the code what I came up after Google'ng for a solution.
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
var temp = range;
var link =prompt("Please enter the link for this selection \n "+range+"","");
range.insertNode(document.createTextNode("<a href='"+link+"' target='_blank'>"+temp+"</a>"));
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
var link =prompt("Please enter the link for this selection \n "+range.text+"","");
range.text = "<a href='"+link+"' target='_blank' onClick='changeLink()'>"+range.text+"</a>";
}
The code doesn't work as I imagined, the link gets added to the content but it is showing the <a> tag also inside the content rather then showing the default blue colored text with underline. How to hide the <a> tag ? also how will I allow them to delete or edit the link.
(UPDATED) Solution :
function addFormat(type) {
var savedSel = saveSelection();
if(savedSel != '') {
var link =prompt("Please enter the link for this selection \n "+savedSel+"","");
restoreSelection(savedSel);
document.execCommand("CreateLink", false, link);
var links = getLinksInSelection();
for (var i = 0; i < links.length; ++i) {
links[i].setAttribute('target','_blank');
}
} else { alert("Please select some text to insert the link"); }
}
function getLinksInSelection() {
var selectedLinks = [];
var range, containerEl, links, linkRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
linkRange = document.createRange();
for (var r = 0; r < sel.rangeCount; ++r) {
range = sel.getRangeAt(r);
containerEl = range.commonAncestorContainer;
if (containerEl.nodeType != 1) {
containerEl = containerEl.parentNode;
}
if (containerEl.nodeName.toLowerCase() == "a") {
selectedLinks.push(containerEl);
} else {
links = containerEl.getElementsByTagName("a");
for (var i = 0; i < links.length; ++i) {
linkRange.selectNodeContents(links[i]);
if (linkRange.compareBoundaryPoints(range.END_TO_START, range) < 1 && linkRange.compareBoundaryPoints(range.START_TO_END, range) > -1) {
selectedLinks.push(links[i]);
}
}
}
}
linkRange.detach();
}
} else if (document.selection && document.selection.type != "Control") {
range = document.selection.createRange();
containerEl = range.parentElement();
if (containerEl.nodeName.toLowerCase() == "a") {
selectedLinks.push(containerEl);
} else {
links = containerEl.getElementsByTagName("a");
linkRange = document.body.createTextRange();
for (var i = 0; i < links.length; ++i) {
linkRange.moveToElementText(links[i]);
if (linkRange.compareEndPoints("StartToEnd", range) > -1 && linkRange.compareEndPoints("EndToStart", range) < 1) {
selectedLinks.push(links[i]);
}
}
}
}
return selectedLinks;
}
function saveSelection() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
var ranges = [];
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
ranges.push(sel.getRangeAt(i));
}
return ranges;
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
}
function restoreSelection(savedSel) {
if (savedSel) {
if (window.getSelection) {
sel = window.getSelection();
sel.removeAllRanges();
for (var i = 0, len = savedSel.length; i < len; ++i) {
sel.addRange(savedSel[i]);
}
} else if (document.selection && savedSel.select) {
savedSel.select();
}
}
}
Thanks.
JS Fiddle
JS
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
console.log(html); <-- returning text even not selected.
}
$(document).ready(function(){
$(document).bind("mouseup", getSelectionHtml);
});
I'm currently trying to understand the following behavior:
1) Select a few lines of text (console.log shows those lines) - expected.
2) Click within the selection you've made. Console.log then shows the same text as the previous, which was selected. - Not expected; here I expect getSelection to return nothing as nothing is currently selected.
Can anyone tell me what i'm missing here?
Thanks!
DEMO jsFiddle
JS
var previousText = '';
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
if(html!= previousText) {
console.log(html);
}
previousText = html;
}
$(document).mousedown(function () {}).mouseup( function () {
getSelectionHtml();
});
To not show empty selections just change:
if(html!= previousText) {
to this:
if(html!= previousText && html != '') {
Note: I'm using jQuery because you were too
I'm using the following function to get selected text and it works very well in all major browsers but it doesn't work correctly in IE before version 9!
function getSelected() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
t = t.toString();
} else if (document.selection) {
t = document.selection.createRange();
t = t.text;
}
return t;
}
var txt = getSelected();
The problem here that with IE before version 9 it doesn't store any text in the variable "txt"
DEMO: http://jsfiddle.net/ytJ35/
The below is taken from How to get selected html text with javascript?
this javascript function works in IE7 and above:
function getSelected() {
var text = "";
if (window.getSelection
&& window.getSelection().toString()
&& $(window.getSelection()).attr('type') != "Caret") {
text = window.getSelection();
return text;
}
else if (document.getSelection
&& document.getSelection().toString()
&& $(document.getSelection()).attr('type') != "Caret") {
text = document.getSelection();
return text;
}
else {
var selection = document.selection && document.selection.createRange();
if (!(typeof selection === "undefined")
&& selection.text
&& selection.text.toString()) {
text = selection.text;
return text;
}
}
return false;
}
Tested in chrome, IE10, IE6, IE7