I'm trying to make a simple text editor so users can be able to bold/unbold selected text. I want to use Window.getSelection() not Document.execCommand(). It does exactly what I want but when you bold any text, you can't unbold it. I want it in a way that I can bold and unbold any selected text. I tried several things but no success.
function addBold(){
const selection = window.getSelection().getRangeAt(0);
const selectedText = selection.extractContents();
const span = document.createElement("span");
span.classList.toggle("bold-span");
span.appendChild(selectedText);
selection.insertNode(span);
};
.bold-span {font-weight: bold;}
<p contentEditable>Bold anything here and unbold it</p>
<button onclick="addBold()">Bold</button>
This is close to what you want but groups words together so an unselect will remove from whole word. I have not been able to complete this as I have to go, but should be a good starting point.
function addBold(){
const selection = window.getSelection().getRangeAt(0);
let selectedParent = selection.commonAncestorContainer.parentElement;
//console.log(parent.classList.contains("bold-span"))
//console.log(parent)
let mainParent = selectedParent;
if(selectedParent.classList.contains("bold-span"))
{
var text = document.createTextNode(selectedParent.textContent);
mainParent = selectedParent.parentElement;
mainParent.insertBefore(text, selectedParent);
mainParent.removeChild(selectedParent);
mainParent.normalize();
}
else
{
const span = document.createElement("span");
span.classList.toggle("bold-span");
span.appendChild(selection.extractContents());
//selection.surroundContents(span);
selection.insertNode(span);
mainParent.normalize();
}
//selection is set to body after clicking button for some reason
//https://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
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();
}
};
.bold-span {font-weight: bold;}
<p contentEditable>Bold anything here and unbold it</p>
<button onclick="addBold()">Bold</button>
var span = '';
jQuery(function($) {
$('.embolden').click(function(){
var highlight = window.getSelection();
if(highlight != ""){
span = '<span class="bold">' + highlight + '</span>';
}else{
highlight = span;
span = $('span.bold').html();
}
var text = $('.textEditor').html();
$('.textEditor').html(text.replace(highlight, span));
});
});
You could define a function like this where the name of your class is "embolden"
Related
I want to build a unique, simple text editor. With this JS code I can get the selected text from the textarea, but how may I give styling to it (font-weight, font-style etc.) with buttons (Bold, Italic)?
var selectedText = '';
function getText(e) {
selectedText = (document.all) ? document.selection.createRange().text : document.getSelection();
alert(selectedText);
}
document.onmouseup = getText;
if (!document.all) document.captureEvents(Event.MOUSEUP);
I experimented with adding the following code into the getText function. It works at some point, the selected text is wrapped into a span, the span gets the .new-span, bold-style and italic-style classes (btn onclick), but still the styling does not apply to the selected text in the textarea, the change is not visible.
var selectedText = '';
function getText(e) {
selectedText = (document.all) ? document.selection.createRange().text : document.getSelection();
var newSpan = document.createElement("span");
newSpan.classList.add("new-span");
newSpan.innerText += selectedText;
console.log(newSpan);
artParag.appendChild(newSpan);
var boldStyleBtn = document.querySelector(".bold-style-btn");
var italicStyleBtn = document.querySelector(".italic-style-btn");
boldStyleBtn.addEventListener("click", function boldStyle() {
newSpan.classList.toggle("bold-style");
alert(selectedText);
});
italicStyleBtn.addEventListener("click", function italicStyle() {
newSpan.classList.toggle("italic-style");
alert(selectedText);
});
}
document.onmouseup = getText;
if (!document.all) document.captureEvents(Event.MOUSEUP);
(the CSS):
.bold-style {
font-weight: 700;
}
.italic-style {
font-style: italic;
}
Is this a good direction? Thank you in advance for your help.
I have a contenteditable div and i would like to add some html tags around highlighted text, after user select the text and click the button..
Here is the sample code. It has some javascript codes but i couldnt make it work as desired. And i played with a lot actually.
https://codepen.io/anon/pen/ybzzXZ
P.S. I'm going to add , or like html tags after when we solve the how to add html tags around it.
Some of that js codes which i found in stackoverflow.
function getSelectionText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().text;
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
return text;
}
and the other one is
function replaceSelectionWithHtml(html) {
var range;
if (window.getSelection && window.getSelection().getRangeAt) {
range = window.getSelection().getRangeAt(0);
range.deleteContents();
var div = document.createElement("div");
div.innerHTML = html;
var frag = document.createDocumentFragment(), child;
while ( (child = div.firstChild) ) {
frag.appendChild(child);
}
range.insertNode(frag);
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML(html);
}
}
There are several challenges with the problem you present.
First off you need to gather the selected text value. You have posted some examples of that - that is fairly well documented elsewhere so I will leave that up to you to isolate that issue.
Next you need to highlight the selected text. Often to highlight something in HTML we wrap that text that we wish to highlight in a simple element such as a span, then give that span some class - for example often this is used to give a background color to some text. <span style='background-color:yellow'>some text</span> - not so difficult to understand that portion.
The challenge with this then is to combine your "discovered text" with the highlight. Pretty easy to wrap that text as in the span example provided earlier. One issue however is that if that text is previously within some other HTML elements, we need to ensure that the text choice in the discovery is for example not contained within another element AND if so, handle that issue. Let's illustrate that with this span: Howdy <span style='background-color:yellow'>some text</span> Some more.
Now for this example suppose we wish to highlight the text "Howdy some" - a portion of that text is previously within a span with our desired markup, thus we must first extract that, remove that "highlight" and henceforth highlight the new text "choice" of "Howdy some".
To provide an illustration of that. Type the words "This I want" into the text box and see how it gets highlighted.
This is not exactly your problem however it provides the "highlight" which you could potentially combine with your selector. I have NOT fully vetted this for bugs such as typing in HTML in to "highlight".
/* highlight words */
function findStringLimit(searchChar, searchCharIndex, searchedString) {
return searchedString.substring(0, searchedString.lastIndexOf(searchChar, searchCharIndex));
};
function highlightWords(wordsy, text) { /* eliminate a bug with parenthesis */
wordsy = wordsy.replace("(", "");
wordsy = wordsy.replace(")", ""); /* escape other characters for bug */
text = text.replace(";", "");
text = text.replace("'", "'");
text = text.replace("<", "<");
text = text.replace(">", ">");
text = text.replace("<span", "<span");
text = text.replace('autoCompleteWord">', 'autoCompleteWord">');
text = text.replace("</span", "</span");
text = text.replace('span>', 'span>');
var re = '(' + wordsy + ')(?![^<]*(?:<\/span class=\"autoCompleteWord\"|>))';
var regExp = new RegExp(re, 'ig');
var sTag = '<span class="autoCompleteWord">';
var eTag = "</span>";
return text.replace(regExp, sTag + '$&' + eTag);
};
function parseAndHighlight(wordstring, htmlString) {
var htmlStringUn = htmlString;
var found = htmlStringUn.toLowerCase().indexOf(wordstring.toLowerCase(), 0);
if (found >= 0) {
htmlStringUn = highlightWords(wordstring, htmlStringUn);
}
else {
//split and parse the beast
var words = wordstring.split(/\W+/);
var allPhrases = [];
allPhrases.push(wordstring);
var i = 0;
i = words.length;
while (i--) {
allPhrases.push(findStringLimit(" ", allPhrases[(words.length - i) - 1].length, allPhrases[(words.length - i) - 1]));
};
i = allPhrases.length;
while (i--) {
if (allPhrases[i] != "") words = words.concat(allPhrases[i]);
};
i = words.length;
while (i--) {
htmlStringUn = highlightWords(words[i], htmlStringUn);
};
};
return htmlStringUn;
}
$(document).on('change', '#myInput', function() {
var myValue = $('#myInput').val(); //get what was typed
$('#found').text(myValue);
myValue = myValue.replace(/^\s+|\s+$/g, ""); //strip whitespace on ends
$('#found').text(myValue + ':stripped:');
var showText = $('#origshower').text();
var newShowString = parseAndHighlight(myValue, showText); //my original highlighter
$('#shower').html(newShowString);
});
#holder{border:red solid 2px; padding: 5px;}
#myInput{width:200px; background-color: aqua;}
span.autoCompleteWord /* this is the word(s) found */
{
font-weight: bold;
background-color: yellow;
}
#shower{border:lime 2px solid;}
<script
src="https://code.jquery.com/jquery-1.12.4.min.js"
integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
crossorigin="anonymous"></script>
<div id='holder'>
<input id='myInput' type='text' cols='60' rows='2' />Enter Text to match
</div>
<div id='origshower'>This is the span thistle with the whistle that I want matched is this neat</div>
<div id='shower'>none</div>
<div id='found'>enter</div>
You can just call executeCommand with formatBlock. You can find more information here:
https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
I have a text on an HTML page. If the user selects a word, I can retrieve the selected word and know exactly what he or she selected. However, I now want to also modify this word on the screen and make it bold. But I do not know how I would do this, since I only know the word clicked, but do not see an easy way to find the word in the HTML and modify it. I could of course search for it, but there is the risk of a word appearing multiple times.
A different way I thought about would be to give each word a unique idea and have something like this:
<span id="1">The</span>
<span id="2">fox</span>
<span id="3">jumps</span>
<span id="4">over</span>
<span id="5">the</span>
<span id="6">fence</span>
But I do not like this solution. This, too, seems overly complicated, does it not? Any suggestions how else I could access the exact words selected?
You can dynamically create a span surrounding the selected word:
const p = document.querySelector('p');
p.addEventListener('mouseup', () => {
const range = document.getSelection().getRangeAt(0);
do {
const charBefore = range.startContainer.textContent[range.startOffset - 1];
if (charBefore.match(/\s/)) break;
range.setStart(range.startContainer, range.startOffset - 1);
} while (range.startOffset !== 0);
do {
const charAfter = range.endContainer.textContent[range.endOffset];
if (charAfter.match(/\s/)) break;
range.setEnd(range.endContainer, range.endOffset + 1);
} while (range.endOffset !== range.endContainer.textContent.length);
const span = document.createElement('span');
span.style.fontWeight = 'bold';
range.surroundContents(span);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>The fox jumps over the fence.</p>
No need of jQuery, also no need of IDs for each <span>.
The idea is to add a class to the span once it is clicked and later you can retrieve all elements with that bolded class.
Here is a solution with pure Javascript:
// comments inline
var spans = document.getElementsByTagName('span'); // get all <span> elements
for(var i=0, l = spans.length; i<l; i++){
spans[i].addEventListener('click', function(){ // add 'click' event listener to all spans
this.classList.toggle('strong'); // add class 'strong' when clicked and remove it when clicked again
});
}
.strong {
font-weight: bold;
}
<span>The</span>
<span>fox</span>
<span>jumps</span>
<span>over</span>
<span>the</span>
<span>fence</span>
Read up: Element.getElementsByTagName() - Web APIs | MDN
$("p").mouseup(function() {
var selection = getSelected().toString();
$(this).html(function(){
return $(this).text().replace(selection, '<strong>' + selection +'</strong>');
});
});
var 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;
}
strong{ font-weight: bold; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>The fox jumps over the fence.</p>
I've created a function that allows a user to align highlighted/selected text right/left/center, but it doesn't seem to be working.
function doCenter() {
{
var selection = window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.style.textAlign = "center";
span.appendChild(selectedText);
selection.insertNode(span);
}
}
I've tried this with a separate function that does the same thing, except it highlights the words instead of aligning them, and it works:
function highlighter() {
{
var selection = window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.style.backgroundColor = "yellow";
span.appendChild(selectedText);
selection.insertNode(span);
}
}
Can anyone tell me what's wrong with the first code?
span is an inline element whereas only block elements may have their child content aligned. Either use div or p instead of a span, or set
span.style.display = 'block';
I'm trying to create a fairly simple text editor (bold, italic, indent) and need to be able to toggle the class associated with the button on click. I have this code:
var selected = function ()
{
var text = '';
if (window.getSelection) {
text = window.getSelection();
}
return text;
}
$('textarea').select(function(eventObject)
{
console.log(selected().toString());
var selectedtext = selected().toString();
$('#bold-button').click(function () {
$(selectedtext).addClass('bold-text');
});
});
And I can get the selected text to print, but can't get the class added. I've seen other solutions that add the class on click to the entire textarea, but I dont need that. Any help?
You could use surroundContents() like below. Before demo here http://jsfiddle.net/jwRG8/3/
function surroundSelection() {
var span = document.createElement("span");
span.style.fontWeight = "bold";
span.style.color = "green";
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
But this is not supported less than IE9. And I worked on text selections before and I found them in consistent. Tim Down is very much experienced on selections and most of the answers in SO related to Selections are given my him. He has written a plugin called rangy. You mat try it at https://code.google.com/p/rangy/
Because you are selecting text directly, there is no element to add the class on. textNodes cannot have classes. Instead, try wrapping the text in an element:
$('textarea').select(function(eventObject) {
console.log(selected().toString());
var selectedtext = selected().toString();
$(selectedtext).wrap('<span />').parent().addClass('bold-text');
})
Or you could just wrap it in a b tag, without the class:
$(selectedtext).wrap('<b/>');