Reputation: 801
My problem is when I want to remove the format of a selected text, the format of the whole text will be removed.
document.getElementById('bold').addEventListener('click', () => edit('STRONG'));
document.getElementById('italic').addEventListener('click', () => edit('EM'));
function edit(format) {
let parentElementOfSelectedText = document.getSelection().getRangeAt(0).commonAncestorContainer;
// If element is already formatted, undo the format
if (parentElementOfSelectedText.tagName === format || parentElementOfSelectedText.parentElement.tagName === format) {
if (parentElementOfSelectedText.tagName !== format) parentElementOfSelectedText = parentElementOfSelectedText.parentElement;
let grandParentOfSelectedText = parentElementOfSelectedText.parentElement;
if (parentElementOfSelectedText.textContent) {
const selectedText = document.createTextNode(parentElementOfSelectedText.textContent);
// const selectedText = document.createTextNode(document.getSelection().toString());
//work with range of old element because
//text nodes are pass by value
//and we cant create a range after its a text node
const range = document.createRange();
range.selectNode(parentElementOfSelectedText);
//this replaces some of your code but uses a range
range.deleteContents();
range.insertNode(selectedText);
grandParentOfSelectedText.normalize();
//select the range again :)
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
} else {
let selectedText = document.getSelection().getRangeAt(0);
const node = document.createElement(format);
const fragment = selectedText.extractContents();
if (fragment) {
node.appendChild(fragment);
}
selectedText.insertNode(node);
//make only the inside of the node a range
//so [...].commonAncestorContainer is "STRONG" or "EM"
//and gets recognized
const range = document.createRange();
range.selectNodeContents(node);
const selection = window.getSelection();
selection.removeAllRanges()
selection.addRange(range);
}
}
<button id="bold">B</button>
<button id="italic">I</button>
<div contentEditable="true">Lorem</div>
Select Lorem and click on the B button. So Lorem will be bold. Now type ipsum after Lorem. Ipsum will also be bold. Select ipsum and click again on the B button. Now not only ipsum lost its format but also Lorem.
It is fine when the text that gets entered will have the same format as the text before. But the user should be able to remove the format of the selected text only.
So when they start to type, the value should be Lorem ipsum and when the remove the format of ipsum, it should be Lorem ipsum.
How can I do this?
I also tried it with
const selectedText = document.createTextNode(document.getSelection().toString());
but then the unselected text will be removed. See JSFiddle
Upvotes: 2
Views: 705
Reputation: 1171
Just get the text selection, and keep track of what style was applied previously (bold/italic). If the style has already been applied, remove the style only from the selected text.
document.getElementById('bold').addEventListener('click', () => edit('STRONG'));
document.getElementById('italic').addEventListener('click', () => edit('EM'));
// Variables to track what style has been applied
let textBold = false;
let textItalic = false;
function edit(format) {
// Getting selected text
let selectedText = getSelection();
// Create new element
let el = document.createElement('span');
// Applying style depending on the format
if (format === 'STRONG') {
// Assignation to retain previous style
el.style.fontStyle = (textItalic? 'italic' : 'normal');
if (textBold === false) {
el.style.fontWeight = 'bold';
textBold = true;
} else {
el.style.fontWeight = 'normal';
textBold = false;
}
}
else if (format === 'EM') {
// Assignation to retain previous style
el.style.fontWeight = (textBold ? 'bold' : 'normal');
if (textItalic === false) {
el.style.fontStyle = 'italic';
textItalic = true;
} else {
el.style.fontStyle = 'normal';
textItalic = false;
}
}
el.innerHTML = selectedText.toString();
let range = selectedText.getRangeAt(0);
range.deleteContents();
range.insertNode(el);
}
<button id="bold">B</button>
<button id="italic">I</button>
<div contentEditable="true">Lorem</div>
Upvotes: 1
Reputation: 3219
window.getSelection()
returns an object that you are trying to then run functions that return undefined.
You should therefore get the actual string of the selected text to work with.
window.getSelection().toString();
Upvotes: 1