As the execCommand() is no longer recommended for new projects, how should I implement it?
What I've tried is to wrap the selected text in a tag and the removing it. It works fine as long as the user doesn't make it bold and then select half of bold and half of non-bold text...it crashes.
function bold(){
var selection = window.getSelection();
var selRange = selection.getRangeAt(0);
if (selection != "") {
let bold = document.createElement("strong");
selRange.surroundContents(bold);
//if it's already bode, the above two lines would have generated another <strong> tag
if (bold.childElementCount>0) {
//removing the child <strong> tag
bold.innerHTML = bold.firstChild.innerHTML;
let parent = bold.parentNode;
text = parent.innerHTML.replace(`<strong>${selection}</strong>`, selection);
parent.innerHTML = text;
}
}
}
Related
I'm having problems running an action on more than one line.
In practice I do not get the desired effect and <p> tags are completely emptied of their content. This is the simple code I use, but it only works on a line:
Try to look at code in action HERE .
Follow the instructions in the example to understand the real problem.
function press() {
if (document.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount < 1) {
return;
}
var r = window.getSelection().getRangeAt(0);
var selectedText = r.toString(),
content = r.extractContents();
span = document.createElement('span'),
t = document.createTextNode(selectedText);
selectedText = '';
span.style.color = 'green';
span.appendChild(t);
r.insertNode(span);
window.getSelection().removeAllRanges();
}
}
<p>Select only me for first: i don't have problems</p>
<p>Also select me at the same time of first paragraph, now i have problems..</p>
<button onclick="press()">Press after selecting the text</button>
I want to do something similar to what this website and wordpress does. When a user highlights text on the screen, then clicks a button on the toolbar it will wrap an html tag around the text. In jquery I would probably use the .wrap class but how would I detect if the user highlighted something.
For example, when the user writes Hello World then clicks on the bold button it will say <b>Hello World</b>
This mainly requires (1) accessing the selectionStart and selectionEnd properties of the input/textarea element and (2) replacing the substring of the value property across that range with the same text, but wrapped in the desired start and end tags. Also, I think it makes sense to reselect the replaced text, which requires a couple of calls to select() and setSelectionRange(). Also, if there's no selection (meaning start equals end) it's probably a good idea to do nothing at all.
window.selWrapBold = function(id) { selWrap(id,'<b>','</b>'); };
window.selWrapItalic = function(id) { selWrap(id,'<i>','</i>'); };
window.selWrap = function(id,startTag,endTag) {
let elem = document.getElementById(id);
let start = elem.selectionStart;
let end = elem.selectionEnd;
let sel = elem.value.substring(start,end);
if (sel==='') return;
let replace = startTag+sel+endTag;
elem.value = elem.value.substring(0,start)+replace+elem.value.substring(end);
elem.select();
elem.setSelectionRange(start,start+replace.length);
} // end selWrap()
<input type="button" value="bold" onclick="selWrapBold('ta1');"/>
<input type="button" value="italic" onclick="selWrapItalic('ta1');"/>
<br/>
<textarea id="ta1"></textarea>
Get the text of the html element which is wrapping the text, then add as html the text embedded in the <b> tag.
See jQuery DOM Manipulation for tutorials.
I used this question to get the selected text. And this question to
get the element with selected text in it. I combined them in a single function.
function updateHighlightedText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
var node = $(window.getSelection().anchorNode.parentNode); //Get the selected node
node.html(node.text().replace(text, "<b>"+text+"</b>")); //Update the node
}
I have a contenteditable div (with id 'editor1') that allows users to input text. There is then a function that allows them to color any highlighted text. My js uses window.getSelection().getRangeAt(0), but the issue with this is that they can highlight words outside of the div and their color will change as well. So far; I've tried:
function red(){
{
var getText = document.getElementById("editor1").innerHTML;
var selection = getText.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(selectedText);
selection.insertNode(span);
}
}
Fiddle: https://jsfiddle.net/xacqzhvq/
As you can see, if I highlight "this will become red as well", I can use the button to make that red too.
How can I only color the highlighted text only within the editor1 div?
You are able to get the node element from the selection using .baseNode. From there you can get the parent node and use that for comparison.
function red(){
// If it's not the element with an id of "foo" stop the function and return
if(window.getSelection().baseNode.parentNode.id != "foo") return;
...
// Highlight if it is our div.
}
In the example below I made the div have an id that you can check to make sure it's that element:
Demo
As #z0mBi3 noted, this will work the first time. But may not work for many highlights (if they happen to get cleared). The <span> elements inside the div create a hierarchy where the div is the parent elements of many span elements. The solution to this would be to take traverse up through the ancestors of the node until you find one with the id of "foo".
Luckily you can use jQuery to do that for you by using their .closest() method:
if($(window.getSelection().baseNode).closest("#foo").attr("id") != "foo") return;
Here is an answer with a native JS implemented method of .closest().
Are you looking for this,
//html
<body>
<p id='editor1'>asdf</p>
<button onclick='red()'>
RED
</button>
</body>
//JavaScript
window.red = function(){
//var getText = document.getElementById("editor1").innerHTML;
var selection = window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(selectedText);
selection.insertNode(span);
}
Plunker: https://plnkr.co/edit/FSFBADoh83Pp93z1JI3g?p=preview
Try This Code :
function addBold(){
if(window.getSelection().focusNode.parentElement.closest("#editor").id != "editor") return;
const selection = window.getSelection().getRangeAt(0);
let selectedParent = selection.commonAncestorContainer.parentElement;
let mainParent = selectedParent;
if(selectedParent.closest("b"))
{
//Unbold
var text = document.createTextNode(selectedParent.textContent);
mainParent = selectedParent.parentElement;
mainParent.insertBefore(text, selectedParent);
mainParent.removeChild(selectedParent);
mainParent.normalize();
}
else
{
const span = document.createElement("b");
span.appendChild(selection.extractContents());
selection.insertNode(span);
mainParent.normalize();
}
if (window.getSelection) {
if (window.getSelection().empty) { // Chrome
window.getSelection().empty();
} else if (window.getSelection().removeAllRanges) { // Firefox
window.getSelection().removeAllRanges();
}
} else if (document.selection) { // IE?
document.selection.empty();
}
};
<div id="editor" contenteditable="true">
You are the programmers of the future
</div>
<button onclick="addBold()">Bold</button>
I got the code and added my edits from those following answers :
Bold/unbold selected text using Window.getSelection()
getSelection().focusNode inside a specific id doesn't work
How can I add <span> tags around selected text within an element?
For example, if somebody highlights "John", I would like to add span tags around it.
HTML
<p>My name is Jimmy John, and I hate sandwiches. My name is still Jimmy John.</p>
JS
function getSelectedText() {
t = (document.all) ? document.selection.createRange().text : document.getSelection();
return t;
}
$('p').mouseup(function(){
var selection = getSelectedText();
var selection_text = selection.toString();
console.log(selection);
console.log(selection_text);
// How do I add a span around the selected text?
});
http://jsfiddle.net/2w35p/
There is a identical question here: jQuery select text and add span to it in an paragraph, but it uses outdated jquery methods (e.g. live), and the accepted answer has a bug.
I have a solution. Get the Range of the selecion and deleteContent of it, then insert a new span in it .
$('body').mouseup(function(){
var selection = getSelectedText();
var selection_text = selection.toString();
// How do I add a span around the selected text?
var span = document.createElement('SPAN');
span.textContent = selection_text;
var range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(span);
});
You can see the DEMO here
UPDATE
Absolutly, the selection will be delete at the same time. So you can add the selection range with js code if you want.
You can simply do like this.
$('body').mouseup(function(){
var span = document.createElement("span");
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
});
Fiddle
Reference Wrapping a selected text node with span
You can try this:
$('body').mouseup(function(){
var selection = getSelectedText();
var innerHTML = $('p').html();
var selectionWithSpan = '<span>'+selection+'</span>';
innerHTML = innerHTML.replace(selection,selectionWithSpan);
$('p').html(innerHTML);
});
and In your fiddle you are again opening a new <p> instead of a closing </p>. Update that please.
THIS WORKS (mostly*)!! (technically, it does what you want, but it needs HALP!)
JSFiddle
This adds <span ...> and </span> correctly, even if there are multiple instances of the selection in your element and you only care about the instance that's selected!
It works perfectly the first time if you include my commented line. It's after that when things get funky.
I can add the span tags, but I'm having a hard time replacing the plaintext with html. Maybe you can figure it out? We're almost there!! This uses nodes from getSelection. Nodes can be hard to work with though.
document.getElementById('d').addEventListener('mouseup',function(e){
var s = window.getSelection();
var n = s.anchorNode; //DOM node
var o = s.anchorOffset; //index of start selection in the node
var f = s.focusOffset; //index of end selection in the node
n.textContent = n.textContent.substring(0,o)+'<span style="color:red;">'
+n.textContent.substring(o,f)+'</span>'
+n.textContent.substring(f,n.textContent.length);
//adds the span tag
// document.getElementById('d').innerHTML = n.textContent;
// this line messes stuff up because of the difference
// between a node's textContent and it's innerHTML.
});
I need to select text from different paragraphs and make a span for showing this text. See this example:
<p> this is a text </p>
<p>hello ever one </p>
Now what I want is that if I select text from the web view in my iPhone app it highlights it in a different color. For this I am making a span and setting its style. It works fine for the same paragraph but not for different paragraphs. See this:
<p> this <span class="blue">is a </span> text </p>
Class blue declares its style and it works fine, but the following does not work:
<span class="blue">
<p> this is a text </p>
<p>hello ever </span> one </p>
For solving this problem I need two spans for both paragraphs. So how can I check where the new paragraph starts? The correct HTML code is:
<span class="blue">
<p> this is a text </p></span>
<p> <span class="blue"> hello ever </span> one </p>
I need to get this HTML string but I get the wrong one. I have written a JavaScript function that gets the selection and makes a span according to selection. But on selecting text from two paragraphs it does not work because it gives the wrong section of HTML code. See my JavaScript code:
function highlightsText()
{
var range = window.getSelection().getRangeAt(0);
var selectionContents = range.extractContents();
var div;
var newDate = new Date;
var randomnumber= newDate.getTime();
var imageTag = document.createElement("img");
imageTag.id=randomnumber;
imageTag.setAttribute("src","notes.png");
var linkTxt = document.createElement("a");
linkTxt.id=randomnumber;
linkTxt.setAttribute("href","highlight:"+randomnumber);
div = document.createElement("span");
div.style.backgroundColor = "yellow";
div.id=randomnumber;
linkTxt.appendChild(imageTag);
div.appendChild(selectionContents);
div.appendChild(linkTxt);
range.insertNode(div);
return document.body.innerHTML+"<noteseparator>"+randomnumber+"<noteseparator>"+range.toString();
}
Please provide a solution that can resolve this problem.
You could do something along the lines of:
Get highlighted section of text.
Insert span tag at the first point.
For every tag that you come accross within the highlighted text:
If it's an opening tag, check if it's corresponding closing tag is in the highlighted text.
If both opening and closing tags are within the text ignore them and move to the next point after the corresponding closing tag.
If only the opening tag or only the closing tag is present, then insert before the tag and after the tag.
Insert span closing tag at the end of the highlighted text.
Possible problem:
span is intented to group inline elements and not block elements so if your highlighted text includes block elements you could have problems. You could use div instead of span to solve this or you could add some checks to distinguish between inline and block tags.
To look at tag matching:
http://haacked.com/archive/2004/10/25/usingregularexpressionstomatchhtml.aspx
To find if the matching closing tag of an element is in the higlighted text (not tested):
function checkClosingTag(position)
{
//Find position of next opening or closing tag along the
//string of highlighted text.
//Return 0 if no more tags.
var nextTag = findNextTag(position);
if(nextTag == 0)
{
return 0;
}
if(!isOpeningTag(nextTag))
{
return nextTag;
}
var nextTagClose = checkClosingTag(nextTag);
if(nextTagClose == 0)
{
return 0;
}
return checkClosingTag(nextTagClose);
}
This looks like a fairly involved problem though - I don't have time to write the code for you but you should be able to work out a way of doing it from here.
some change in your code can work
see this line of codes
function highlightsText()
{
var range, sel;
if (window.getSelection)
{
sel = window.getSelection();
if (sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
if ( !document.execCommand("HiliteColor", false, "yellow") ) {
document.execCommand("BackColor", false, "yellow");
}
document.designMode = "off";
}
else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.execCommand("BackColor", false, "yellow");
}
var newDate = new Date;
var randomnumber= newDate.getTime();
var nodeList = document.querySelectorAll(".Apple-style-span");
for (var i = 0, length = nodeList.length; i < length; i++) {
nodeList[i].id = randomnumber;
}
var div = document.getElementById(randomnumber);
var imageTag = document.createElement("img");
imageTag.id=randomnumber;
imageTag.setAttribute("src","notes.png");
var linkTxt = document.createElement("a");
linkTxt.id=randomnumber;
linkTxt.setAttribute("href","highlight:"+randomnumber);
div.appendChild(linkTxt);
range.insertNode(div);
return document.body.innerHTML+"<noteseparator>"+randomnumber+"<noteseparator>"+range.toString();
}
You need make some adjustments in this code.
Since your goal (based on what you stated in your question) is to highlight selected text with a different color, here is a solution to that goal.
The HTML5BoilerPlate project includes styles to control the selection color (line 52 in the style.css file)
Here's the CSS for it:
/* Remove text-shadow in selection highlight: h5bp.com/i
*
* These selection declarations have to be separate
*
* Also: hot pink! (or customize the background color to match your design)
*/
::-moz-selection { background: #fe57a1; color: #fff; text-shadow: none; }
::selection { background: #fe57a1; color: #fff; text-shadow: none; }