I have contenteditable=true div, where user enters his feedback and I want to allow him change the text color of selected text written in div when user clicks on colorpalate.
I have full code to do my task of changing the color of selected text with user desired color.
But...
Everthing else on the page can be selected and changed its colour too, which I want to prevent. I want to allow change the colour only for class="comment".
Just can't understand how to do that with below code.
if (window.getSelection) { // all browsers, except IE before version 9
var selectionRange = window.getSelection ();
if (selectionRange.isCollapsed) {
alert ("Please select some content first!");
}
else {
//do the stuff
}
}
Thanks for help
UPDATED : to include highlight functionality
Here's the jsFiddle : DEMO
$("#highlight").click(function(){
var flag = 0;
sel = window.getSelection();
for (var i = 0; i < sel.rangeCount; i++) {
var $sNode = $(sel.getRangeAt(i).startContainer.parentNode);
var $eNode = $(sel.getRangeAt(i).endContainer.parentNode);
if ($sNode.prop("class") == "comment" && $eNode.prop("class") == "comment"){
$sNode.html($sNode.html().replace(sel,"<span class='selectedText'>"+ sel + "</span>"));
}
}
});
Will only highlight if text is selected from comment class div only
Related
This is the code on js fiddle for tests https://jsfiddle.net/em7yfa12/
And also code is here:
But as first let me explain what code does. And then I will tell what I need to achieve. I can not find the way by myself. That's why I am asking here for the solution.
When "Open input element to insert text " button is pressed prompt element appears to get text, which will be later painted to the color which was selected in color dialog.
But this event will happen only when color will be changed, because the event is triggered from change in color dialog.
This is all regular. But I need something extra and that is:
If color dialog was opened and no change in color was performed before dialog was closed, then I need to get:
alert("You have to select a different color than any of colors you have selected in previous cases to paint the text");
<input type="button" id="newlabelID" value="Open input element to insert text "/></br>
<input type="color" id="colorDialogID" ></br>
<a id="aID"> Text to paint</br>
var someText;
function createStatusF(){
someText = prompt("Enter some text :", "");
if ((someText=="")||(someText==null)){
return;
}
document.getElementById("colorDialogID").focus();
document.getElementById("colorDialogID").value = "#FFCC00";
document.getElementById("colorDialogID").click();
}
document.getElementById("newlabelID").onclick = createStatusF;
document.getElementById("colorDialogID").style.display = "none";
function peintTheText(){
document.getElementById("aID").innerHTML= someText;
var theColor=document.getElementById("colorDialogID").value;
document.getElementById("aID").style.color=theColor;
}
document.getElementById("colorDialogID").onchange = peintTheText;
You need to use the input event and attach it to the color input. This works, but it's not a good idea to use alert with the color picker, since the color picker will have a higher z index than the alert, hence the user will not be able to click it.
Here's the JS code:
let someText;
let previousColors = [];
let colorDialog = document.getElementById("colorDialogID");
function openColorPicker() {
colorDialog.focus();
colorDialog.value = "#FFCC00";
colorDialog.click();
}
function createStatusF() {
someText = prompt("Enter some text :", "");
if ((someText == "") || (someText == null)) {
return;
}
openColorPicker();
}
document.getElementById("newlabelID").onclick = createStatusF;
document.getElementById("colorDialogID").style.display = "none";
function peintTheText() {
var theColor = colorDialog.value;
previousColors.push(theColor);
document.getElementById("aID").innerHTML = someText;
document.getElementById("aID").style.color = theColor;
}
function onColorSelected(e) {
const theColor = colorDialog.value;
if (previousColors.includes(theColor)) {
alert("You have to select a different color than any of colors you have selected in previous cases to paint the text");
}
}
colorDialog.addEventListener('input', onColorSelected);
colorDialog.addEventListener('change', peintTheText);
Working Fiddle
I'm building a chrome extension where selected text can have different highlighting styles applied to it. I used ranges to get this all to work, and I clone the range, put a span around it, and then delete the range and replace it with the cloned one. Everything seems fine except I've somehow managed to disable right clicking by triggering this behavior through the extension. I've narrowed it down the single line of range.surroundContents(span), but here's the full code section:
// Determines the selected text
document.onmouseup = function() {
var selection = document.getSelection();
selection = getSelectedText(color);
};
// Finds the text selected in the page, spans it, and gives it a class
function getSelectedText(inputColor) {
var span = document.createElement('span');
span.setAttribute('class', inputColor);
if(document.getSelection) {
var selection = document.getSelection();
if(selection.rangeCount == true) {
var range = selection.getRangeAt(0).cloneRange();
range.surroundContents(span);
selection.removeAllRanges();
selection.addRange(range);
}
}
}
Is there a way I can counter this? I've already tried using document.oncontextmenu = false directly following the problem line, but that's not bringing back right click. I also tried replacing it with newNode.appendChild(range.extractContents()); range.insertNode(newNode) as recommended by https://developer.mozilla.org/en-US/docs/Web/API/Range/surroundContents but then instead of highlighting text, it seems to just be removing it from the page.
#wOxxOm answered my question in a comment, but a setTimeout() is what worked. So for anyone else who might have a similar issue in the future:
// Finds the text selected in the page, spans it, and gives it a class
function getSelectedText(inputColor) {
var span = document.createElement('span');
span.setAttribute('class', inputColor);
if(document.getSelection) {
var selection = document.getSelection();
if(selection.rangeCount == true) {
var range = selection.getRangeAt(0).cloneRange();
setTimeout(function(){
range.surroundContents(span);
selection.removeAllRanges();
selection.addRange(range);
}, 100)
}
}
}
I am working on a bookmark app where i have to store the user's selected keywords or words or content. I am using the createRange() and addRange() javascript methods to create the range and then find out the selected elements/contents by the user. The code i written for this is as follow.
<head>
<script type="text/javascript">
var storedSelections = [];
function StoreSelection () {
if (window.getSelection) {
var currSelection = window.getSelection ();
for (var i = 0; i < currSelection.rangeCount; i++) {
storedSelections.push (currSelection.getRangeAt (i));
}
currSelection.removeAllRanges ();
} else {
alert ("Your browser does not support this example!");
}
}
function ClearStoredSelections () {
storedSelections.splice (0, storedSelections.length);
}
function ShowStoredSelections () {
if (window.getSelection) {
var currSelection = window.getSelection ();
currSelection.removeAllRanges ();
for (var i = 0; i < storedSelections.length; i++) {
currSelection.addRange (storedSelections[i]);
}
} else {
alert ("Your browser does not support this example!");
}
}
</script>
</head>
<body>
Select some content on this page and use the buttons below.<br /> <br />
<button onclick="StoreSelection ();">Store the selection</button>
<button onclick="ClearStoredSelections ();">Clear stored selections
</button>
<button onclick="ShowStoredSelections ();">Show stored selections</button>
</body>
This code is working perfectly on Firefox. I am able to select multiple things one by one and able to show the selected content again but on chrome and chromium i am getting Discontiguous selection is not supported. error when i store more than one elements in range array and click on show stored selections button.
Help will be appreciated. And please suggest me if there is some other alternatives do accomplish this bookmarking task.
Write
window.getSelection().removeAllRanges();
immediately before creating range.
https://bugs.chromium.org/p/chromium/issues/detail?id=399791
Here's the only possible way of doing this that I was able to come up with:
Wrap the selection in <span style="background: Highlight;">...</span>.
But note:
Obviously, you have to remove those spans again as soon as anything else is selected, but that shouldn't be too difficult. However, you should use window.onmousedown for that rather than window.onclick, because onclick is fired after any button is pressed, so when pressing your "Show stored selections" button, a new selection will be created, thus destroying the one that was supposed to be captured.
Removing or replacing any elements in which a stored selection starts or ends will invalidate that selection, so when clicking "Show stored selections", nothing will show up.
If the selection spans over multiple elements, it needs to split up into one selection for each element, otherwise inserting the span will either fail or cut other elements (like buttons) in half.
The following code (fiddle) is the best I was able to do:
var storedSelections = [];
var simulatedSelections = [];
window.onmousedown = clearSimulatedSelections;
function storeSelection()
{
if(window.getSelection)
{
var currSelection = window.getSelection();
for(var i = 0; i < currSelection.rangeCount; i++)
{
storeRecursive(currSelection.getRangeAt(i));
}
currSelection.removeAllRanges();
}
else
{
alert("Your browser does not support this example!");
}
}
function storeRecursive(selection, node, started)
{
node = node || document.body;
started = started || false;
var nodes = node.childNodes;
for(var i = 0; i < nodes.length; i++)
{
if(nodes[i].nodeType == 3)
{
var first = nodes[i] == selection.startContainer;
var last = nodes[i] == selection.endContainer;
if(first)
{
started = true;
}
if(started)
{
var sel = selection.cloneRange();
if(!first)
{
sel.setStartBefore(nodes[i]);
}
if(!last)
{
sel.setEndAfter(nodes[i]);
}
storedSelections.push(sel);
if(last)
{
return false;
}
}
}
else
{
started = storeRecursive(selection, nodes[i], started);
}
}
return started;
}
function clearStoredSelections()
{
storedSelections = [];
}
function showStoredSelections()
{
if(window.getSelection)
{
var currSelection = window.getSelection();
currSelection.removeAllRanges();
for(var i = 0; i < storedSelections.length; i++)
{
var node = document.createElement("span");
node.className = "highlight";
storedSelections[i].surroundContents(node);
simulatedSelections.push(node);
}
}
else
{
alert("Your browser does not support this example!");
}
}
function clearSimulatedSelections()
{
for(var i = 0; i < simulatedSelections.length; i++)
{
var sec = simulatedSelections[i];
var pn = sec.parentNode;
while(sec.firstChild)
{
pn.insertBefore(sec.firstChild, sec);
}
pn.removeChild(sec);
}
simulatedSelections = [];
}
.highlight
{
background: Highlight;
}
Select some content on this page and use the buttons below.<br><br>
<button onclick="storeSelection();">Store the selection</button>
<button onclick="clearStoredSelections();">Clear stored selections</button>
<button onclick="showStoredSelections();">Show stored selections</button>
It works in Firefox, Safari and Chrome, but has the following shortcomings:
Selections over multiple lines don't select the blank area between the end of the line and the border of the parent element, like actual selections do.
Sometimes when starting a selection at a point before the start of a stored selection, displaying them will merge the ranges, so the text in between is selected too. Sorting the array of stored selections doesn't seem to help.
In Safari, the tab crashed multiple times with a segmentation fault when selecting multiple lines and ending/starting a selection in the middle of a button's text.
However, I doubt that anything better is possible in browsers other than Firefox, but even Firefox has a ticket to drop discontiguous selections.
FYI I was getting a similar error when rolling my own "copy to clipboard" feature. I'm not going to address OP's provided code, but I'll tell you how I fixed it in my own code.
Reproduce:
Copy some other text on the page to the clipboard, e.g. "foo".
Paste the text somewhere. It outputs "foo".
Click your "copy to clipboard" button, which copies e.g. "bar".
Paste the text somewhere.
Expected:
"bar" is outputted.
Actual:
"Discontiguous selection is not supported"
Fix:
Call window.getSelection().removeAllRanges() at the start of your "copy to clipboard" event handler. "Discontiguous" means "not connected". So my guess is that the browser copies the first range (the node containing "foo"), and then gets angry when you try to select another range that is not next to the first node.
suppose to be we have a paragraph with this content " Hi , It's a new question in stackoverflow!"
and when we are selecting something in this paragraph , it's turn to be Red .for example we selected stackoverflow & then it turn to <span class="red">stackoverflow</span>.how can we do this with Javascript?
here is my codes :
var x = {};
x.getSelected = function() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
} else if (document.selection) {
t = document.selection.createRange().text;
}
return t;
}
$(document).ready(function() {
var selectedText;
$(document).bind("mouseup", function() {
selectedText = x.getSelected()
if (selectedText !=''){
alert(selectedText);
//Now I wanna set new content for selected item but not working
a=selectedText;
selectedText.html("<span class='red'>"+a+"</span>");
}
});
});
.red {
color : red;
}
<p>suppose to be we have a paragraph with this content " Hi , It's a new question in stackoverflow!" and when we are selecting something in this paragraph , it's turn to be Red .for example we selected stackoverflow & then it turn to .how can we do this with Javascript? </p>
...when we are selecting something in this paragraph , it's turn to be
Red...
You could have a stab at the styleWithCSS command of the editing API, execCommand that is.
However, before proceeding please note that:
This spec is incomplete and it is not expected that it will advance
beyond draft status. Authors should not use most of these features
directly, but instead use JavaScript editing libraries. The features
described in this document are not implemented consistently or fully
by user agents, and it is not expected that this will change in the
foreseeable future.... This spec is to meant to help implementations
in standardizing these existing features. It is predicted that in the
future both specs will be replaced by Content Editable Events and
Input Events....
Having clarified that, the following will work in most modern browsers viz. Edge, FireFox and Chrome that I could test in.
By default the foreColor command of execCommand wraps the selected text with a font tag, which is deprecated. So, you need to use the styleWithCSS command. Now this works with the editing API, which means that the element you are trying to work with, should have its contentEditable attribute set.
To work around this, you can temporarily set this attribute just before changing the color in the selected text fragment and then resetting the attribute once done.
Given your paragraph like this:
<p id="p">
Hi , It's a new question in StackOverflow!
</p>
When you select the word StackOverflow, the following code will result in this...
<p id="p">
Hi , It's a new question in <span style="color: rgb(255, 0, 0);">StackOverflow</span>!
</p>
... wrapping your selected text in a span with the style applied.
Fiddle: http://jsfiddle.net/abhitalks/j9w6dj7m/
Snippet:
p = document.getElementById('p');
p.addEventListener('mouseup', setColor);
function setColor() {
p.setAttribute('contentEditable', true);
document.execCommand('styleWithCSS', false, true);
document.execCommand('foreColor', false, "#f00");
p.setAttribute('contentEditable', false);
}
<p id="p" contentEditable="false">
Hi , It's a new question in stackoverflow!
</p>
Edit:
Now that you have added code (and what you have already tried) in your question, you could use the range selection to do what you are after.
Specifically, you will need to learn:
selection: https://developer.mozilla.org/en-US/docs/Web/API/Selection, this you have already done. Cheers!
range: https://developer.mozilla.org/en-US/docs/Web/API/Range/Range, because you will be dealing with ranges here
selection.getRangeAt(): https://developer.mozilla.org/en-US/docs/Web/API/Selection/getRangeAt, because you will need to extract the selected text as a range object
range.surroundContents(): https://developer.mozilla.org/en-US/docs/Web/API/range/surroundContents, because you will need to surround the selected text range with a span.
Putting it all together all you have to do is (explanation in code comments):
function setClass() {
var selection = x.getSelected(), range, // you have already done this
span = document.createElement("span"); // create a span element
span.classList.add('red'); // add the class to the span
if (selection != '') {
range = selection.getRangeAt(0); // get the range from selected text
range.surroundContents(span); // surround the range with span
}
}
Fiddle 2: http://jsfiddle.net/abhitalks/kn0u5frj/
Snippet 2:
var x = {},
p = document.getElementById('p');
p.addEventListener('mouseup', setClass);
function setClass() {
var selection = x.getSelected(), range,
span = document.createElement("span");
span.classList.add('red');
if (selection != '') {
range = selection.getRangeAt(0);
range.surroundContents(span);
}
}
x.getSelected = function() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
} else if (document.selection) {
t = document.selection.createRange().text;
}
return t;
}
.red { color: #f00; }
<p id="p">
Hi , It's a new question in stackoverflow!
</p>
You can use the getSelection() method
Below is the example:
Repeated Question:
How to get selected text in textarea?
You can use CSS with :: selection http://caniuse.com/#search=%3A%3Aselection
::selection {
background: #ffb7b7; /* WebKit/Blink Browsers */
}
::-moz-selection {
background: #ffb7b7; /* Gecko Browsers */
}
Or javascript with range
We have an editable div and we want to change the style of the text, selected by the user, on clicking our button; just same as this editor's bold or italic button.
But when we click on the button our selected text get deselected.
So how to keep our selected text highlighted even if the focus from the editable is off and change the style.
My Code :
Editable Div:
var textbox= document.createElement("div");
textbox.setAttribute("contenteditable", "true");
Button :
var bold = document.createElement("div");
bold.innerHTML = "B";
Button Click:
bold.addEventListener("click", function(){
getSelectedText();
},false);
function getSelectedText()
{
var html = "";
if (window.getSelection) {
html = window.getSelection().toString();
}
else if (document.selection && document.selection.type != "Control") {
html = document.selection.createRange().text;
}
alert(html);
}
You can get the selection by listening to your contentEditable's onmouseup and store it in a variable. After you clicked a div, you restore the selection to back what it was:
Javascript:
var range = "";
function getSelected() {
var selection = window.getSelection()
range = selection.getRangeAt(0);
}
function reselect() {
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
jsfiddle DEMO
used a button and a div for buttons so you can see the difference.
Either use the mousedown event instead of the click event or set the <div> button to be unselectable.
See this answer for details.