Contenteditable paragraphs in html - javascript

I am creating a contenteditable paragraph in html. I have a button which on clicking will make the text bold. The first time it is clicked the text should change to bold,and the next time it is clicked ,the text should be normal(not bolder).This is similar to the Stack overflow question editor.html code:
<button type="button" id="bold" onclick="bold()">B</button>
<div id="content">
<p id="hey" contenteditable="true">Hi how are you</p>
</div>
JS:
let boldClick=0;
let p=document.getElementById('hey');
function bold(){
if(boldClick%2==0){
p.innerHTML=p.innerHTML+' <span contenteditable="true">'+' boldtext'+'</span>';
}
else{
let pNew=document.createElement('p');
pNew.setAttribute("contenteditable","true");
document.getElementById('content').appendChild(pNew);
}
boldClick++;
}
CSS:
#bold{
font-weight: bold;
}
span{
font-weight: bold;
background-color: grey;
}
p{
display: inline;
}
I can make the text bolder by clicking the button.Bolder texts must be inside the span element and non-bolder texts must be outside span but inside
p element.How do I solve it?

approach 1:
let boldClick=0;
let p=document.getElementById('hey');
p.innerHTML=p.innerHTML+' <span contenteditable="true" id="bold">'+' boldtext'+'</span>';
const boldTag = document.getElementById('bold');
function bold(){
if(boldClick%2==0){
boldTag.style.fontWeight="";
}
else{
boldTag.style.fontWeigh="bold";
}
boldClick++;
}
approach 2:
let boldClick=0;
let p=document.getElementById('hey');
p.innerHTML=p.innerHTML+' <span contenteditable="true" id="bold">'+' boldtext'+'</span>';
const boldTag = document.getElementById('bold');
function bold(){
if(boldClick%2==0){
boldTag.id="";
}
else{
boldTag.id="bold";
}
boldClick++;
}
these approaches are not optimized , but hope you get the concept .

the shortest way. Create a class named Bold. Add the properties you want to this class. Select elements with Javascript and assign a function to have this class.
In general , creating a class with css and using javascript is the simplest way to solve problems, which element of this class should be added when.
let clicked = false;
function bold() {
let paragraph = document.getElementById("paragraph");
let span = document.getElementById("span");
if (clicked) {
span.contentEditable = false;
span.classList.remove("bold");
clicked = false;
} else {
span.contentEditable = true;
span.classList.add("bold");
clicked = true;
}
}
p {
display: inline;
}
.bold {
font-weight: bold;
}
<button type="button" id="bold-button" onclick="bold()">B</button>
<div id="content">
<p id="paragraph">Hi how are you
<span id="span">how are you</span>
</p>
</div>

First off, please tag your post with java script instead of java, similar name, different language. Here is the code you should use:
let isBold = false;
let p=document.getElementById('hey');
function bold(){
if(isBold == false){
p.style.fontWeight = "bold";
isBold = true;
}
else{
p.style.fontWeight = "normal";
isBold = false;
}
}
It changes the font weight (if it is bold or not) instead of the complex code you had. If you are just turning something on and off (bold or not) then use a boolean value (true + false) true will make it bold, false will make it normal. For HTML graphics, you should use CSS language. You can change the variable's CSS values by saying style then the thing you want to change.

Related

getSelection add html, but prevent nesting of html

I have a content editable field, in which I can enter and html format som text (select text and click a button to add <span class="word"></span> around it).
I use the following function:
function highlightSelection() {
if (window.getSelection) {
let sel = window.getSelection();
if (sel.rangeCount > 0) {
if(sel.anchorNode.parentElement.classList.value) {
let range = sel.getRangeAt(0).cloneRange();
let newParent = document.createElement('span');
newParent.classList.add("word");
range.surroundContents(newParent);
sel.removeAllRanges();
sel.addRange(range);
} else {
console.log("Already in span!");
// end span - start new.
}
}
}
}
But in the case where I have:
Hello my
<span class="word">name is</span>
Benny
AND I select "is" and click my button I need to prevent the html from nesting, so instead of
Hello my
<span class="word">name <span class="word">is</span></span> Benny
I need:
Hello my
<span class="word">name</span>
<span class="word">is</span>
Benny
I try to check the parent class, to see if it is set, but how do I prevent nested html - close span tag at caret start position, add and at the end of caret position add so the html will not nest?
It should also take into account if there are elements after selection which are included in the span:
So:
Hello my
<span class="word">name is Benny</span>
selecting IS again and clicking my button gives:
Hello my
<span class="word">name</span>
<span class="word">is</span>
<span class="word">Benny</span>
Any help is appreciated!
Thanks in advance.
One way would be to do this in multiple pass.
First you wrap your content, almost blindly like you are currently doing.
Then, you check if in this content there were some .word content. If so, you extract its content inside the new wrapper you just created.
Then, you check if your new wrapper is itself in a .word container.
If so, you get the content that was before the selection and wrap it in its own new wrapper. You do the same with the content after the selection.
At this stage we may have three .word containers inside the initial one. We thus have to extract the content of the initial one, and remove it. Our three wrappers are now independent.
function highlightSelection() {
if (window.getSelection) {
const sel = window.getSelection();
if (sel.rangeCount > 0) {
const range = sel.getRangeAt(0).cloneRange();
if (range.collapsed) { // nothing to do
return;
}
// first pass, wrap (almost) carelessly
wrapRangeInWordSpan(range);
// second pass, find the nested .word
// and wrap the content before and after it in their own spans
const inner = document.querySelector(".word .word");
if (!inner) {
// there is a case I couldn't identify correctly
// when selecting two .word start to end, where the empty spans stick around
// we remove them here
range.startContainer.parentNode.querySelectorAll(".word:empty")
.forEach((node) => node.remove());
return;
}
const parent = inner.closest(".word:not(:scope)");
const extractingRange = document.createRange();
// wrap the content before
extractingRange.selectNode(parent);
extractingRange.setEndBefore(inner);
wrapRangeInWordSpan(extractingRange);
// wrap the content after
extractingRange.selectNode(parent);
extractingRange.setStartAfter(inner);
wrapRangeInWordSpan(extractingRange);
// finally, extract all the contents from parent
// to its own parent and remove it, now that it's empty
moveContentBefore(parent)
}
}
}
document.querySelector("button").onclick = highlightSelection;
function wrapRangeInWordSpan(range) {
if (
!range.toString().length && // empty text content
!range.cloneContents().querySelector("img") // and not an <img>
) {
return; // empty range, do nothing (collapsed may not work)
}
const content = range.extractContents();
const newParent = document.createElement('span');
newParent.classList.add("word");
newParent.appendChild(content);
range.insertNode(newParent);
// if our content was wrapping .word spans,
// move their content in the new parent
// and remove them now that they're empty
newParent.querySelectorAll(".word").forEach(moveContentBefore);
}
function moveContentBefore(parent) {
const iterator = document.createNodeIterator(parent);
let currentNode;
// walk through all nodes
while ((currentNode = iterator.nextNode())) {
// move them to the grand-parent
parent.before(currentNode);
}
// remove the now empty parent
parent.remove();
}
.word {
display: inline-block;
border: 1px solid red;
}
[contenteditable] {
white-space: pre; /* leading spaces will get ignored once highlighted */
}
<button>highlight</button>
<div contenteditable
>Hello my <span class="word">name is</span> Benny</div>
But beware, this is just a rough proof of concept, I didn't do any heavy testings and there may very well be odd cases where it will just fail (content-editable is a nightmare).
Also, this doesn't handle cases where one would copy-paste or drag & drop HTML content.
I modified your code to a working approach:
document.getElementById('btn').addEventListener('click', () => {
if (window.getSelection) {
var sel = window.getSelection(),
range = sel.getRangeAt(0).cloneRange();
sel.anchorNode.parentElement.className != 'word' ?
addSpan(range) : console.log('Already tagged');
}
});
const addSpan = (range) => {
var newParent = document.createElement('span');
newParent.classList.add("word");
range.surroundContents(newParent);
}
.word{color:orange}button{margin:10px 0}
<div id="text">lorem ipsum Benny</div>
<button id="btn">Add span tag to selection</button>

How to continously bold the text in a div while typing

I have a to do app and I was requested to add more functionality, such as: add some buttons for Bold and Italic text. In a input, after I press the Bold button, the text that is going to be typed to be bolded, leaving the previous text (the one before I pressed the Bold button) the way it was, regular.
I understood that there's no way to bold a section of text in a input, so I simulated a input with a div :
const div = document.querySelector('div');
div.innerHTML = '';
const bold = document.getElementById('boldBtn');
const italic = document.getElementById('italicBtn')
bold.style.backgroundColor = 'white';
let previousText = '';
let boldString = '';
boldBtn.addEventListener('click', () => {
boldBtn.classList.toggle('bold-selected');
if (boldBtn.classList.contains('bold-selected')) {
boldBtn.style.backgroundColor = "gray";
previousText = div.innerHTML;
console.log(previousText)
div.addEventListener('keydown', boldText)
}
else {
bold.style.backgroundColor = "white";
div.removeEventListener('keydown', boldText);
}
})
function boldText(e) {
div.innerHTML = div.innerHTML.substr(1)
console.log("Previous text: " + previousText);
const NOT_ALLOWED = ['Backspace', 'Shift', 'Control', 'Alt'];
if (!NOT_ALLOWED.includes(e.key)) {
boldString += e.key;
console.log("Bold text: " + boldString)
console.log(previousText + boldString)
div.innerHTML = previousText + "<strong>" + boldString + "</strong>"
}
}
div {
border: 1px solid black;
width: 200px;
height: 20px;
}
.font-style {
border: 1px solid blue
}
<div contenteditable="true"></div>
<button id="boldBtn" class="font-style">Bold</button>
<button id="italicBtn" class="font-style">Italic</button>
Try to write something like "cool" in the div then press the Bold button, then type a letter. You'll see that the div gets this innerHTML: letter+cool+boldletter. And the cursor is set at the beginning of the div content. Please help me or at least give a hint to accomplish the wanted behavior! I spent 3-4 hours already and I am ready to give up...
EDIT:
I think I didn't make it clear: I don't want to make the entire content of the div be bold, but just a portion/section/part of it. If no button is pressed, the text that is gonna be written should be regular, if the Bold button is pressed, the text that is gonna be written should be Bold, the same with the Italic button. Maybe if the Bold and Italic are selected at the same time, the future text should be bold and italic. But that's another question... An example of what I want is https://html-online.com/editor/ . Remove the default text and just write words on the left panel, and try to use the bold, italic buttons above. In the left panel there will be the HTML generated code. I need the same functionality...
In order to make text bold when you select "bold", get range from current selection and insert element "strong" in it.
let range=window.getSelection.getRangeAt(0);
let elem=document.createElement('strong');
elem.innerHTML='​'
range.insertNode(elem);
this will make the text bold, same applies for the italic option too. Now the problem comes when you need to disable it, for that i had to clear current ranges in selection and set range to end of "div" and insert new element
"span"(i used span for normal text).
Likewise i handled below cases too
when bold and italic are pressed together( You need to store
previous Element in this case)
when enter is pressed( div was
getting created and was creating problems so i had to set keydown
listener and use document.execCommand to not create div instead
create br)
If anyone is interested in updating my solution to make it effective you are welcome
var boldButton=document.querySelector('#boldBtn'),
italicButton=document.querySelector('#italicBtn'),
contentDiv=document.querySelector('#content'),
bold=false,italic=false,range,prevElement,selection;
;
contentDiv.addEventListener('keydown',function(e){
if (e.keyCode === 13) {
window.document.execCommand('insertLineBreak', false, null);
range=window.getSelection().getRangeAt(0);
range.collapse();
prevElement=undefined;
if(bold) addElement('strong');
if(italic) addElement('i');
e.preventDefault();
return false;
}
return true;
});
boldButton.addEventListener('click',function(){
debugger
bold=!bold;
boldButton.classList.toggle('border-black');
if(!bold)
disable('bold');
else
addElement('strong');
});
italicButton.addEventListener('click',function(){
debugger;
italic=!italic;
italicButton.classList.toggle('border-black');
if(!italic)
disable('italic');
else
addElement('i');
});
function addElement(element){
if(!selection)selection=window.getSelection()
range=selection.getRangeAt(0);
let elem=document.createElement(element);
elem.innerHTML='​'
if(prevElement)
range.selectNodeContents(prevElement);
range.collapse();
range.insertNode(elem);
elem.focus();
prevElement=elem;
}
function disable(elem){
if(italic && bold) return;
setRangeAtEnd();
let element=document.createElement('span');
if(italic)
element=document.createElement('i');
else if(bold)
element=document.createElement('strong');
element.innerHTML='​'
range.insertNode(element);
range.setStart(element,1);
element.focus();
}
function setRangeAtEnd(){
let childNodes=contentDiv.childNodes;
range=document.createRange();
range.setStartAfter(childNodes[childNodes.length-1]);
prevElement=undefined;
if(!selection)selection=window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
#content{
border:1px solid black;
height:150px;
}
.border-black{
border:2px dotted black;
}
<div id="content" contenteditable="true"></div><br>
<button id="boldBtn" class="font-style">Bold</button>
<button id="italicBtn" class="font-style">Italic</button>
Yes there is a way out for this as you can use jquery for this like
<script>
$(document).ready(function () {
$("#boldBtn").click( function() {
$("your input id or class").keyup(function () {
$("your input id or class").css("fontWeight", "bold");
});
});
});
</script>
Where your input id or class mentioned if you have class then put it as .classname or if you have an id write is as #id.
Hope this might help you.

both buttons trigger one script

I have a button that triggers a script on a webpage. One instance works. When I try to add a second button/script, both buttons trigger the second script only. I know (think?) it's because the var I'm defining for the buttons are not unique to their individual scripts, but every way I attempt I break the whole thing.
button {
display: block;
cursor: pointer;
margin-left: 10px;}
button:after {
content: " (off)";
}
button.on:before {
content: "✓ ";
}
button.on:after {
content:" ";
}
.frac span {
-webkit-font-feature-settings: "frac" 1;
font-feature-settings: "frac" 1;
}
.onum span {
-webkit-font-feature-settings: "onum" 1;
font-feature-settings: "onum" 1;
}
Html:
<button name="frac" id="frac">Fractions</button>
<button name="onum" id="onum">Oldstyle Numbers</button>
This text is supposed change OT features when the buttons are pressed.
JS:
<script> var btn = document.getElementById("frac"),
body = document.getElementById("textA"),
activeClass = "frac";
btn.addEventListener("click", function(e){
e.preventDefault();
body.classList.toggle(activeClass);
btn.classList.toggle('on');
}); </script>
<!-- onum -->
<script> var btn = document.getElementById("onum"),
body = document.getElementById("textA"),
activeClass = "onum";
btn.addEventListener("click", function(f){
f.preventDefault();
body.classList.toggle(activeClass);
btn.classList.toggle('on');
}); </script>
The variance between the scripts/buttons are some of the changes from different things I've done, but I've gone mostly back to the beginning so it's simpler.
In javascript, every variable that you declare is inherently available across the entire page. So, putting them in separate tags will have no effect.
So essentially, your second variable btn is actually overwriting the first one. Rename the second variable to say, btn2.
Or, as an alternative, change the line
btn.classList.toggle('on')
to
this.classList.toggle('on')
this within the click handler will always point to the current button being clicked.
You can do it in fewer lines of code
// you create the array of buttons
let butons = Array.from(document.querySelectorAll("button")),
// you define the _body
_body = document.getElementById("textA")
// for every button in the buttons array (map is an iterator)
butons.map((btn) =>{
//you define the activeClass to be the name attribute of the button
let activeClass = btn.getAttribute("name");
// everytime you click the button
btn.addEventListener("click", (e) =>{
/*this was in your code. I don't know why you need it
e.preventDefault();*/
//you toggle the activeClass & the on class
_body.classList.toggle(activeClass);
btn.classList.toggle("on");
})
})
button {
display: block;
cursor: pointer;
margin-left: 10px;
}
button:after {
content: " (off)";
}
button.on:before {
content: "✓ ";
}
button.on:after {
content: " ";
}
/* I'm using color to visualize the change */
.frac span {
color: red;
}
.onum span {
color: green;
}
<button name="frac" id="frac">Fractions</button>
<button name="onum" id="onum">Oldstyle Numbers</button>
<p id="textA">The variance between the <span>scripts/buttons</span> are some of the changes from different things I've done, but I've gone mostly back to the beginning so it's simpler.</p>

Getting text selection with javascript, then modify text

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>

make the search textbox in dataTables toUpperCase

I want to make the string of Search Textbox of dataTables to Upper Case, not only the css, but also the string.
Can I do that ?
*note : I want the upper case as the default, even when the capslock not active
To all CSS answers, the question states that the actual property must be uppercase, not just appear that way through styling.
Here is a quick vanilla JavaScript example that should run on any modern browser:
//Function to change case
function upperCaseValue(element) {
//Test that passed element has a "value" field
if( typeof element.value !== void 0) {
//Overwrite value
element.value = element.value.toUpperCase();
}
}
//Fetch elements to watch
var search = document.getElementById("search");
var output = document.getElementById("output");
//Bind events
search.addEventListener("keyup", function(){
//Change case
upperCaseValue(search);
//Use input
output.innerHTML = search.value;
});
search.addEventListener("change", function(){
//Change case
upperCaseValue(search);
//Use input
output.innerHTML = search.value;
});
#search {
text-transform: uppercase;
}
<input name="search" id="search" />
<p id="output"></p>
Add this style to your input element.
HTML
<input name="search" id="search" />
CSS
#search {
text-transform: uppercase;
}
just add this styling
.dataTables_wrapper .dataTables_filter input
{
text-transform:uppercase;
}
fiddle link fiddle

Categories

Resources