Any good javascript range highlighter - javascript

I have been struggling for a long time looking for a range highlighter. If the user selects a particular text, that should be highlighted and in the same way if the use selects a range of text and click on remove highlight it should remove only the highlight from that selection. Please help me , i am stuck with this for a long time . Thanks.

You don't exactly need a plugin to do this, instead, you can get the element for where you the selected/highlighted text is, and then stored its content in a variable. Then you can set the elements .innerHTML back to the stored value. In the example below I have applied this to the entire body so it removed any selections made on the entire webpage.
See working example below:
const btn = _ => {
const elem = document.querySelector('body');
const txt = elem.innerHTML;
elem.textContent = '';
elem.innerHTML = txt;
}
<body>
<p id="txt">This is some text. This is some more text, this is even mooooore text, this is even mooooore text, this is even mooooore text, this is even mooooore text, this is even mooooore text! this is even mooooore text! this is even mooooore text!</p>
<button onclick="btn()">Deselect</button>
</body>

Related

Allow moving out of a span at the end of a contenteditable

I'm trying to write a basic text editor using contenteditable. In this MCVE, it only has one function, which is that selected text is given a red highlight.†
The code I'm using is here:
function createSpan() {
let selection = document.getSelection();
let range = selection.getRangeAt(0);
let element = document.createElement("span");
element.className = "inline-equation";
range.surroundContents(element);
let newRange = new Range();
newRange.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(newRange);
}
$("button").click(createSpan)
.inline-equation {
background-color: red;
display: inline-block;
}
#editor {
width: 100%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>
Create Span
</button>
<div id="editor" contenteditable="true">
This is a contenteditable area.
</div>
I'm having trouble with the idea that the user may move out of the highlight area and continue typing in unformatted text. To experience this issue:
Run the Stack Snippet above
Select the text from somewhere in the middle til the end, then click Create Span
Type some new text at the end of the line
This new text has the red highlight too, even if you attempt to move out of the inserted span by pressing the right arrow key.
I'd still like to give the user the option to append new text which is formatted, but then also allow the user to navigate out of the span so that they may continue to type normal text.
In other words, the span should act as a completely separate editable object which may be moved into or out of. This includes the ability to move out of the span even if it's at the end of the document, so that the user can continue typing in non-formatted text.
The best example I am able to give of what I'd like is Microsoft Word's inline equations. Notice how, in the GIF below, the equation acts as a separate object, which I may navigate out of so that I can type normal text to the right of it. The is how I'd like my span to act.
I've tried replacing the span with a div with inline-block formatting to see if that affected the behaviour, but it didn't. How should I achieve the effect I'm looking for?
† In the actual use case, the 'highlight' actually denotes LaTeX-formatted mathematics which are rendered later. I'm writing what is essentially an editor for a proprietary markup language which supports inline LaTeX.
The issue is that you need something editable at end for this to work. There are lot of existing SO thread for the same. You can see below
Why Is My Contenteditable Cursor Jumping to the End in Chrome?
contenteditable put caret outside inserted span
contenteditable with nested span. Who has the focus?
Focusing on nested contenteditable element
Combining knowledge from above thread the simplest thing I could think of was adding below keyup handler
$("#editor").on('keyup',(e) => {
var editor = $("#editor").get(0)
var cn = editor.childNodes;
if (cn[cn.length - 1].nodeType !== Node.TEXT_NODE)
{
empty = document.createTextNode( '\uFEFF' );
editor.appendChild(empty);
}
if (cn[0].nodeType !== Node.TEXT_NODE)
{
empty = document.createTextNode( '\uFEFF' );
editor.prepend(empty);
}
})
Which makes sure there is one text node to step out of the div. You can do the same thing for the starting div if you want. Below is JSFiddle for the same
https://jsfiddle.net/4tcLr0qa/1/

How to style text via a javascript function when coming from an input field

I have one input field that facilitates a person having a conversation but playing both roles in the convo. I want to get as close as I can to what its like to have a text conversation, but I cannot seem to sort out how to style the text when it comes through.
As of the moment, the user types the text and hits one of two buttons, each is loaded with the following function to pull the text, create a div, text node, append them and place in the page.
I tried styling the initial input but that simply makes the input field styled, does not affect the actual output.
I tried adding style at each step of the way, to the variable I saved the input in, to the p, the div, the text node, and after placing it in the doc... each time the function failed.
I tried the attribute method and an innerhtml approach.
What would work? At minimum I would love the function to bold and right align the text. Next best would be to append it with the contents of an ng-app so it says Me: (text here), then My future self: (text here)... which I sense would just involve a string set to a variable.. but setting x = {{name}} caused the function to fail..
I know theres a way to use firebug to understand these failures, but I am not quite understanding that yet. Any suggestions?
<script>
function changeTextComment4(destination){
// to be modified from the above to change the location of the dump
// this function ADDS a comment from the comment field to the div w id comment near it...
var userInput = document.getElementById('userInputS1').value;
// get the input from the user
// 3 make the div a panel
var para = document.createElement("P");
// assignment of attributes
var t = document.createTextNode(userInput);
para.appendChild(t);
// add comment area
// place the item
var destination = document.getElementById(destination)
destination.insertBefore(para, destination.firstChild);
document.getElementById('userInputS1').value = "";
document.getElementById('userInputS1').focus();}
</script>
you can add style by referring to the selector
#userInputS1{
color : #F00;
}

HTML5 how to get selected text out of a textarea

I was able to get the highlighted text out of a textarea by recording onselect and storing the beginning and end each time. Then, when I click a button, I build the substring myself. Isn't there a simpler way of simply querying the selection?
I was kind of expecting that there would be methods in html5 dom for all these things, something like:
textarea.getSelectedStart()
textarea.getSelectedEnd();
textArea.setSelected(start,end);
Also, is there a way of programmatically deselecting text in a textarea?
I am putting in code based on the first solution below. This sort of works, but has a weird problem:
<script language=javascript>
function replaceCLOZE(code, questionType) {
var v = code.value;
var s = code.selectionStart;
var e = code.selectionEnd;
var t = v.substr(s, e-s);
var rep = "{:" + questionType + ":="+t+"}";
code.value = v.substr(0,s) + rep + v.substr(e,v.length-e+1);
}
function shortAnswer(code) {
replaceCLOZE(code, "SA");
}
function shortAnswerCaseSensitive(code) {
replaceCLOZE(code, "SAC");
}
function multipleChoice(code) {
replaceCLOZE(code, "MC");
}
The text area does in fact have attributes code.selectionStart and code.selectionEnd. But the code above, which now works, sets the highlighted text on the screen to be the first word in the textarea. Mind you, the selectionStart is still correct, but what is actually highlighted in Firefox is wrong.
In Chrome it works fine. Maybe this is just a bug in firefox or is there something else which should be done to properly update the textarea visually?
Following is simple way to get selected text of textarea of html. Still not clear what you want as following method simply will give you selected text in alert.
<html><head>
<script>
function alertme(){
var textarea = document.getElementById("textArea");
var selection = (textarea.value).substring(textarea.selectionStart,textarea.selectionEnd);
alert (selection);
}
</script>
</head>
<body>
<p><textarea class="noscrollbars" id="textArea"></textarea></p>
<button onclick="alertme()">Click me</button>
</body></html>
When you select text, the button will alert you what you have selected. selectionStart gives you starting point and selectionEnd gives you end point, while substring needs three arguments string, starting point and ending point.

Change back color of several contiguous HTML tags when user selects them

I'm developing an Extension for Google Chrome. It has a text highlighting facility when user select a certain text area.
When it comes to a single tag I can just use a <span> tag inside that particular tag.
so far what i have done is below.
var divs = document.getElementsByTagName("p");
for(var d in divs) {
// highlight a part of a <p> tag
divs[d].addEventListener('mouseup',function(){var str= document.getSelection() ;alert(str);
this.innerHTML =this.innerHTML.replace(str,'<span style="background-color: '+'yellow'+' ">'+str+'</span>');
});
}
But if user select several tag areas (contiguous tags) how can I do it. I can't put a single <span> tag there. I have to use <span> tag for each of those selected tags.
1) how to detect and get user selected tags. And the starting points and end points.?
2) how to change back color of them at once.?
3) do i want to add listeners , If so how should i do that ?
Any help will be appreciate .
I have understood the following:
Your extension changes the background color of the text (of the underlying paragraph-nodes) the user has selected in the webpage.
Currently your implementation adds an EventListener to inject a span tag inside a paragraph-tag if the Event mouseup fires.
You would like to know how to highlight the selected area (the nodes) if the user has selected more than a single leaf-node?
Have i understood you correctly?
As far as i have understood your question you could do the following.
If multiple nodes are selected you could set the background-color on each node within the selected text:
Based on this html:
<h2>How to set background color of selected text</h2>
<div>
<h3 id="h">This is a headline</h3>
<p id="p1">This is the first paragraph</p>
<p id="p2">This is the second paragraph</p>
<button id="b1" onclick="colorSelectedNodes()">color selected nodes</button>
</div>
You could use the following Javascript:
function colorSelectedNodes(){
var ds= document.getSelection();
var elements = []
// only start node if selection occured from start to end
// (from top left to right bottom)
var anchorNode = ds.anchorNode.parentNode;
var elementSibling = anchorNode.nextElementSibling;
var focusNode = ds.focusNode.parentNode; // endnode
while(elementSibling.nextElementSibling){
console.log("elementSibling", elementSibling);
elementSibling.style.backgroundColor = "#00FFFF";
elementSibling.style.fontWeight = "bold";
elementSibling.style.color = "blue";
elementSibling = elementSibling.nextElementSibling;
}
// just to show focus and end node
anchorNode.style.backgroundColor = "lightgreen";
focusNode.style.backgroundColor = "yellow";
console.log("anchorNode", anchorNode);
console.log("focusNode", focusNode);
}
There are a few thinks to take care of
if the user selects from end to start anchorNode and focusNode are switched,
above i do not check if the selection is within a single node (for example inside a span) or if the selection contains many nodes
according to another question chrome (webkit) changed the api of document.getSelection();
cross browser is a bumby ride; but you are looking for a chrome only solution this should not be to relevant for you
I hope my answer gets you going in the right direction. If you have more questions feel free to ask.

Div Editable and More... More

Well,
I need to replace a word, in a div contentEdible property on, by the same word but formatted...
Like this:
<div> My balls are big </div>
To this (replace the word: balls):
<div> My <font style="color:blue;">balls</font> are big </div>
In a contentEditable this happens dinamically, while the user type the text the replacements happens. I think that a simple event onkeydown, onkeyup, or onkey press, can solve this part.
But, the trouble is with the caret, that after all that i tryed, it stay before the word replaced, when should be stay after. I tryed write some js code, tryed find some jquery scripts, but all them failed in this situation...
Any one has some ideia, or trick ?
I think:
--> Record the length of the word unformatted.
--> Delete this word
--> Put new word formatted.
--> Walk with the caret, to position based this formatted word length.
--> Is it?
PS: I have to considerate a word in any place of this div.
I don't know how to write this code that do what i say above.
Correct me, if i'm wrong.
Since yet, thanks!
Edit[1]: I want that this works on Mozilla Firefox, specificlly;
I only have IE6/7 on this machine, but maybe you can apply the concept to other browser versions of Ranges (or maybe this is cross-browser?).
Basically we store the cursor position, make our search/replacement, then put the cursor back where it was:
html:
<div id="content" contentEditable="true" onkeyup="highlight(this)">This is some area to type.</div>
and the script:
function highlight(elem) {
// store cursor position
var cursorPos=document.selection.createRange().duplicate();
var clickx = cursorPos.getBoundingClientRect().left;
var clicky = cursorPos.getBoundingClientRect().top;
// copy contents of div
var content = elem.innerHTML;
var replaceStart = '<font style="color:blue">';
var replaceEnd = '</font>';
// only replace/move cursor if any matches
// note the spacebands - this prevents duplicates
if(content.match(/ test /)) {
elem.innerHTML = content.replace(/ test /g,' '+replaceStart+'test'+replaceEnd+' ');
// reset cursor and focus
cursorPos = document.body.createTextRange();
cursorPos.moveToPoint(clickx, clicky);
cursorPos.select();
}
}

Categories