Determine node name from text selection - javascript

I have a use case where a user can select some text in a p element and I need to toggle 'bold' it - ie, if it's NOT bold, make the text bold and vice-versa. Is there a way to determine the node type/name from the selected text?
For example: in the below example, when the user selects 'Foo' I want to know that a span has been selected. If the user selects 'Bar'; I want to know that a p was selected. If the user selects 'Foo B'; I want to know that a p was selected.
$('#toggle-bold').click(function() {
// The following will always add a new span wrapping the selected text then make it bold
var selection= window.getSelection().getRangeAt(0);
// Anyway to determine the node type/name of the selected text?
var selectedText = selection.extractContents();
var span= document.createElement("span");
span.appendChild(selectedText);
selection.insertNode(span);
$(span).css('font-weight', 'bold');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p contenteditable="true">
<span>Foo</span> Bar
</p>
<button id="toggle-bold">Toggle Bold</button>

The Range object has two properties for this: startContainer (the node where the range starts) and endContainer (the node where it ends). Then you can get the node name via the nodeName property. (You may or may not want to use parentNode if the node in question is a Text node.) So for instance, since your selection variable actually references a Range (not a Selection):
console.log("Start node's nodeName: " + selection.startContainer.nodeName);
console.log("End node's nodeName: " + selection.endContainer.nodeName);
Often, again, those will show "#text" because the range may be in a Text node.
$('#toggle-bold').click(function() {
// The following will always add a new span wrapping the selected text then make it bold
var selection = window.getSelection().getRangeAt(0);
// Anyway to determine the node type/name of the selected text?
console.log("Start node's nodeName: " + selection.startContainer.nodeName);
console.log("End node's nodeName: " + selection.endContainer.nodeName);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.appendChild(selectedText);
selection.insertNode(span);
$(span).css('font-weight', 'bold');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p contenteditable="true">
<span>Foo</span> Bar
</p>
<button id="toggle-bold">Toggle Bold</button>
Range also tells you where in those container elements the range starts and ends (via startOffset and endOffset).

Related

Selected text remove element already wrapped in a span

I have a scenario/dilemma, I am a little stuck trying to figure out. Below will be my best of a visual representation.
1. I select
This is dummy text to select
2. I bold and it gets wrapped with a span
This is dummy text to select
<h2 This is dummy <span style="font-weight: 700;">text to select</span></h2>
3. Now lets say I want to select some text but we know it's already wrapped in a span
This is dummy [BOLD]text[BOLD] to select
4. Its a crossover with selected text, so now my code. In my important comment, I can identify that the element is found in the selection. But how can I find it and then remove the span.
const el = document.createElement('span');
function handleNodeWrap(){
if(selection?.containsNode(el,true)){
//! IMPORTANT find and remove element of selected text
}
surroundSelection(el);
}
Here's one approach to remove the span from the selected text:
const el = document.createElement('span');
function handleNodeWrap() {
if (selection?.containsNode(el, true)) {
// find parent node of the selected text
let parentNode = selection.anchorNode.parentNode;
// create a new range that selects the entire content of the parent node
let range = document.createRange();
range.selectNodeContents(parentNode);
// replace the content of the parent node with the plain text version
let text = range.extractContents().textContent;
parentNode.textContent = text;
} else {
surroundSelection(el);
}
}

getRangeAt(0) returns #text node with parentNode document-fragment

I'm trying to get the selected node. Let's say I got all selected "CLICK TO EDIT".
<span style="text-transform: lowercase">CLICK TO EDIT</span>
Attempt to grab the current selection through:
var select = window.getSelection();
var range = select.getRangeAt(0).cloneRange();
var selectedNode = range.cloneContents().childNodes[0];
In some cases, selectedNode is a #text node containing "CLICK TO EDIT" instead of a SPAN element.
What I want is the span containing this text node. But selectedNode.parentNode is a document-fragment, selectedNode.parentElement is null.
How am I supposed to get the span element in this case then?
This code listens for selectionchange events, and gets the parent element of the selected text.
document.addEventListener('selectionchange', e => {
var selection = window.getSelection();
var anchorNode = selection.anchorNode;
if (anchorNode) {
// This is the <span>, when you select the text
var parentElement = anchorNode.parentElement;
console.log(parentElement);
}
});
<span style="text-transform: lowercase">CLICK TO EDIT</span>

Javascript add class to selected text, not first occurrence of the selected text

I want to add a class (and later on to send that string to php) to a text with javascript. Whenever I try to do that, the code is adding the class to the first occurrence of my selection, not to the actual selection. Keep in mind that I want to send that EXACT selection to php (and put it in a database as well so it keep that class even after refresh).
JQ
$("#highlight").click(function(){
paraval = $('#para').html();
sel = window.getSelection();
newst = '<a class="selectedText">' + sel + '</a>';
newvalue = paraval.replace(sel, newst);
$('#para').html(newvalue);
});
HTML
<p>Will only highlight if text is selected from comment class div only</p>
<div class="comment" id="para" contenteditable="true">Here goes some text Here goes some text Here goes some text Here goes some text
Some other text</div>
<input type="button" value="Highlight" id="highlight"/>
CSS
.selectedText{
background-color:yellow;
}
.comment{
border: solid 2px;
}
.comment::selection {
background-color: yellow;
}
example here: http://jsfiddle.net/zq1dqu3o/3/
try to select the last occurrence of the word "text". the first one will get the class "selectedText"...
thanks
Call me lazy, but if you don't mind span being you selection marker tag, you can use rangy's cssApplier class.
var cssApplier;
$(document).ready(function() {
rangy.init();
cssApplier = rangy.createCssClassApplier(
"selectedText", {normalize: true,
applyToEditableOnly:true});
});
$("#highlight").click(function(){
if(cssApplier != undefined)
{
cssApplier.toggleSelection();
}
});
I use applyToEditableOnly here to make it only work in that specific div. (I'm not sure how cross-browser compatible that particular setting is. Worked in Chrome and Firefox though.) This uses position rather than selection text to decide what to mark.
JS Fiddle Here: http://jsfiddle.net/zq1dqu3o/7/
You can get the last occurence with lastIndexOf() and proceed like this:
$("#highlight").click(function(){
paraval = $('#para').text();
sel = "text";
var n = paraval.lastIndexOf(sel);
var before = paraval.substring(0,n);
newst = before + '<a class="selectedText">' + sel + '</a>';
newvalue = paraval.replace(paraval, newst);
$('#para').html(newvalue);
});
Just created a fiddle for it: Replacing last occurence
Note: This quick example is only working because the word you want to highlight is at the last position of the text, but you can check out if this solution is ok for your request. In case the last occurence of the word is elsewhere, just create a variable "after" that contains the text following the last occurence of the word to the end.
Have just provided an example for this in updated fiddle: Replacing last occurence update
with following update to previous code:
var after = paraval.substring(n + sel.length, paraval.length);
newst = before + '<a class="selectedText">' + sel + '</a>' + after;

How to select the text of a span on click?

I am looking for a way to select the text inside a span using jquery when the text is clicked on.
For example in the html snippet below, I want the text "\apples\oranges\pears" to become selected when it is clicked on.
<p>Fruit <span class="unc_path">\\apples\oranges\pears</span></p>
I've tried implementing this myself to no avail.
It could be implemented with native JavaScript. A working demonstration on jsFiddle. Your code could be like this:
$('.unc_path').click(function (){
var range, selection;
if (window.getSelection && document.createRange) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(this);
selection.removeAllRanges();
selection.addRange(range);
} else if (document.selection && document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(this);
range.select();
}
});
You can use CSS to do this more easily than JS with style="user-select: all;"
add cursor: pointer; so its obvious they can click...
See code snippet:
<p>
Fruit
<span style="user-select: all; cursor: pointer;">\\apples\oranges\pears</span>
</p>
A working demonstration : http://jsfiddle.net/dystroy/V97DJ/
$('.unc_path').click(function (){
var text = $(this).text();
var $input = $('<input type=text>');
$input.prop('value', text);
$input.insertAfter($(this));
$input.focus();
$input.select();
$(this).hide();
});​
The idea (see comment above) is to dynamically replace the span with an input, only cross-browser way I know to have selected text.
Note that this is only half the road, as you probably want to deselect, style to remove border, etc.
And I must also precise that an input, contrary to a span, cannot span on multiple lines.
I don't think this could/should be used in a real application except in a very specific point.
EDIT : new version : http://jsfiddle.net/dystroy/A5ZEZ/
In this version the text comes back to normal when focus is lost.
$('.unc_path').click(function (){
var text = $(this).text();
var $this = $(this);
var $input = $('<input type=text>');
$input.prop('value', text);
$input.insertAfter($(this));
$input.focus();
$input.select();
$this.hide();
$input.focusout(function(){
$this.show();
$input.remove();
});
});​
To select the specific Span you need a id to be provided to that span. Else you need to loop through the list of all available span to get it.
Lets take this as Example (have added id attribute)
<p>Fruit <span class="unc_path" id="span1">\\apples\oranges\pears</span></p>
The JQuery will be like this
$('span1').text() // if you want to take the text
$('span1').html() // if you want to take the html

How to remove span onclick or on modify?

How do I write a JS function to remove the span on click and just retain the inner text ?
​<span class="test" onclick="removespan(this);">Data in red</span>
​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
​removespan = function(e)​{
alert(e.innerText);
}​
CSS : ​span.test{color:red;}
onclick I would like to remove the span and just retain the inner text .
Infact I would like to have a onmodify event ...that removes the span .
The purpose is to remove a spellchecker span class in WYSIWYG editor .
If the span is the only child element inside its parent node
<div>
<span class="test" onclick="removespan(this);">Data in red</span>
</div>
removespan = function(span) {
span.parentNode.innerHTML = span.innerHTML;
}
Otherwise use this function
removespan = function(span) {
span.parentNode.replaceChild(document.createTextNode(span.innerHTML), span);
}
In case anyone is interested in an "expanded" version of Diode's second option:
function removespan(span) {
// Get the text contents of the clicked span
var span_contents = span.innerHTML;
// Get the parent node of the clicked span
var span_parent = span.parentNode;
// Create a new text node in the DOM to hold the text contents
var text_node = document.createTextNode(span.innerHTML);
// Replace the clicked span node with your newly created text node
span_parent.replaceChild(text_node, span);
// Alternatively, do the above in one line...
// span.parentNode.replaceChild(document.createTextNode(span.innerHTML), span);
}
<span id="test"></span>
y=doc.getElementsById("test");
y.getParent().removeChild(y);

Categories