I'm making a function that takes a selected piece of text and highlights it by adding the tag. It highlights the text but if I click the highlight button again, the text disappears. Instead, I'd just want the text to go back to being un-highlighted. Anyone have suggestions how I could go about doing this?
highLightText() {
var selection = window.getSelection();
console.log('this is the selection: ', selection);
var selection_text = selection.toString();
var mark = document.createElement('mark');
mark.textContent = selection_text;
var range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(mark);
}
My idea was to somehow check if the selected text from var selection has an element named mark in it and if it does, don't continue with the rest of the function. I'm just not exactly sure how to implement it.
You can get a reference to the node that is currently selected by using the Selection's anchorNode or focusNode and from there get the parent element of the text.
var parent = selection.anchorNode.parentElement;
From there is it just a matter of checking it's nodeName property to see if it is mark.
parent.nodeName == "MARK" //nodeName is uppercase
After that just replace the mark element with the text node and call normalize() on the grand parent element to make sure any adjacent text nodes get merged
let grandParent = parent.parentElement;
parent.replaceWith(document.createTextNode(parent.textContent));
grandParent.normalize();
This will do all the text in the mark element and not just the selected text of a mark, for that you will have to modify the code if that was the intention.
Demo (hit any key after making a selection)
function highLightText() {
var selection = window.getSelection();
var parent = selection.anchorNode.parentElement;
var selection_text = selection.toString();
if (parent.nodeName == "MARK") {
let grandParent = parent.parentElement;
parent.replaceWith(document.createTextNode(parent.textContent));
grandParent.normalize();
return;
}
var mark = document.createElement('mark');
mark.textContent = selection_text;
var range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(mark);
}
document.addEventListener('keyup',highLightText);
<div>
<div>Test one two three</div>
<div>Test four five six</div>
<div>Test seven eight nine</div>
<div>Test ten eleven twelve</div>
</div>
Related
I want a feature where clicking the Edit button will make the text content inside span tags editable. I was able to do that but couldn't figure out how to get the blinking cursor at the end of text.
Below is the simplified version of my code.
document.querySelector('button').addEventListener('click', function(){
const span=document.querySelector('span');
span.setAttribute('contentEditable', 'true');
span.focus();
let val=span.innerText;
span.innerText='';
span.innerText=val
})
<span>this is the span tag</span> <button>Edit</button>
Create a new range object set the node you wish to set the range selection to addRange using getSelection... See notes in code snippit
https://developer.mozilla.org/en-US/docs/Web/API/Document/createRange
https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection
https://developer.mozilla.org/en-US/docs/Web/API/Range/selectNodeContents
https://developer.mozilla.org/en-US/docs/Web/API/Selection/removeAllRanges
document.querySelector('button').addEventListener('click', function() {
const span = document.querySelector('span');
span.setAttribute('contentEditable', 'true');
span.focus();
let val = span.innerHtml;
span.innerHtml = '';
span.innerHtml = val;
//set a new range object
let caret = document.createRange();
//return the text selected or that will be appended to eventually
let sel = window.getSelection();
//get the node you wish to set range to
caret.selectNodeContents(span);
//set collapse to null or false to set at end of node
caret.collapse(null);
//make sure all ranges are removed from the selection object
sel.removeAllRanges();
//set all ranges to null and append it to the new range object
sel.addRange(caret);
})
<span>this is the span tag</span> <button>Edit</button>
*This post may also be helpful to you...
How to set caret(cursor) position in contenteditable element (div)?
It makes the whole text bold. I want only selected text to get bold (Strictly no exec Command)
let boldBtn = document.getElementById('Bold-Btn');
let boldClickListener = (event) =>
{
event.preventDefault();
let selection = window.getSelection();
let final = `<span class="text-bold">${selection.focusNode.textContent}</span>`;
selection.anchorNode.parentElement.innerHTML=final;
console.log(selection);
};
boldBtn.addEventListener('click',boldClickListener);
One way of doing this might be to do the following:
Get the window selection.
Convert the selection to a string to get the text.
Create the element that will be bold.
Replace the selected text contained in the innerHTML of the parentElement with the bolded element.
Example based on the code you provided:
let boldBtn = document.getElementById('Bold-Btn');
let boldClickListener = (event) => {
event.preventDefault();
// Get selection
let selection = window.getSelection();
// Get string of text from selection
let text = selection.toString();
// Create bolded element that will replace the selected text
let final = `<span class="text-bold">${text}</span>`;
// Replace the selected text with the bolded element
selection.anchorNode.parentElement.innerHTML = selection.anchorNode.parentElement.innerHTML.replace(text, final);
};
boldBtn.addEventListener('click', boldClickListener);
.text-bold {
font-weight: bold;
}
<div>
Test this text
</div>
<button id="Bold-Btn">
Bold
</button>
Note that you may want to add some more logic when creating your bold element to handle if any existing text is already bold.
I'm looking into text selection and ranges in JavaScript.
I need to get any nodes that surround the selected text exactly, for example:
<div>this is <span>some simple</span> text</div>
When the user selects the words 'some simple' i need to know that it sits entirely within the node .
Yet if they select just 'some' then this is not entirely within the node as the word 'simple' is NOT selected.
The end requirement is to be able to amend the class on the node only if the whole text within the node is selected.
jquery is also viable. thanks
To add some more context to this, when a user selects some text we add some sytling to it, let's say 'bold'. the user can edit the text in the parent div as often as they wish so each edit could add a new span enclosing the selected text. We could end up with something like this:
<div><span class="text-bold">Hi</span>, <span class="text-red">this <span class="text-italic">is</span></span> a sample text item</div>
So the spans can come and go dependant on what the user wants.
You are looking to get the DOM each time. So you can set id to your HTML Element Objects and get the value from them. For example:
<span id="f_span">some simple</span>
<script>
var x = document.getElementById("f_span");
</script>
Then you can check if x value equals with the value that user had selected.
You can use setInterval to get the selected text every x second. With the joined function you can get the selected text.
For the selection of user :
function getSelected() {
if(window.getSelection) { return window.getSelection(); }
else if(document.getSelection) { return document.getSelection(); }
else {
var selection = document.selection && document.selection.createRange();
if(selection.text) { return selection.text; }
return false;
}
return false;
}
It return an object that give you the offset of the selection. If result.anchorOffset = 0 and result.focusOffset = result.anchorNode.length (if it start at the begining of the node and it have the length of the whole node), then the user selected all your node.
Thanks for your replies, it allowed me to cobble together my solution:
function applyTextFormatClass(className) {
var selection = getSelected();
var parent = selection.getRangeAt(0).commonAncestorContainer; //see comment and link below
if (parent.nodeType !== 1) {
parent = parent.parentNode; //we want the parent node
}
var tagText = parent.innerText;
var selectText = selection.toString();
if (tagText.length !== selectText.length) {
addNodeAroundSelectedText(selection, className); //create new node
} else {
addClass(parent, className); //add class to existing node
}
}
commonAncestorContainer: https://developer.mozilla.org/en-US/docs/Web/API/Range/commonAncestorContainer
I have a string with a word five times.if i selected forth hello it should return 4
<div id="content">hello hai hello hello hello</div>
getting selected text script
<script>
if(window.getSelection){
t = window.getSelection();
}else if(document.getSelection){
t = document.getSelection();
}else if(document.selection){
t =document.selection.createRange().text;
}
</script>
If am selecting hai it should return 1.
If am selecting hello hai it should return 1. please help.
Assuming that the contents of the <div> are guaranteed to be a single text node, this is not too hard. The following does not work in IE < 9, which does not support Selection and Range APIs. If you need support for these browsers, I can provide code for this particular case, or you could use my Rangy library.
Live demo: http://jsfiddle.net/timdown/VxTfu/
Code:
if (window.getSelection) {
var sel = window.getSelection();
var div = document.getElementById("content");
if (sel.rangeCount) {
// Get the selected range
var range = sel.getRangeAt(0);
// Check that the selection is wholly contained within the div text
if (range.commonAncestorContainer == div.firstChild) {
// Create a range that spans the content from the start of the div
// to the start of the selection
var precedingRange = document.createRange();
precedingRange.setStartBefore(div.firstChild);
precedingRange.setEnd(range.startContainer, range.startOffset);
// Get the text preceding the selection and do a crude estimate
// of the number of words by splitting on white space
var textPrecedingSelection = precedingRange.toString();
var wordIndex = textPrecedingSelection.split(/\s+/).length;
alert("Word index: " + wordIndex);
}
}
}
You'll need to use the Range capabilities of the DOM. Here is how to get the currently selected range:
var currentRange = window.getSelection().getRangeAt(0);
From there currentRage.startOffset will tell you the position of your selection within the file. So you'll need to compare that with the start range of your element:
var myContent = document.getElementById('content');
var divRange = document.createRange ();
divRange.selectNodeContents (myContent);
Now you can compare the divRange.startOffset with your selection startOffset and determine which one you're on.
I'm using the rangy library and can select text in a content editable as follows:
var sel = rangy.getSelection();
alert(sel);
I can't figure out how to get the selected text parent node/element. For example, if I'm selecting text that is
<strong>My Text</strong>
or
<h1>My Title</h1>
how can I include the strong node or H1 element also?
sel.anchorNode.parentNode will get you the parent node of the node containing only one end of the selection. To get the innermost containing element for the whole selection, the easiest thing is to get a Range from the selection and look at its commonAncestorContainer property (which may be a text node, in which case you need to get its parent):
var sel = rangy.getSelection();
if (sel.rangeCount > 0) {
var range = sel.getRangeAt(0);
var parentElement = range.commonAncestorContainer;
if (parentElement.nodeType == 3) {
parentElement = parentElement.parentNode;
}
}