I am working on a WYSIWYG editor in which I want to add <p>'s when hit Enter / Return key and then the user will write to this new <p>.
Right now I am having issue setting the caret to this new <p>.
$('#content').on('keypress', function(e){
if(e.which === 13){
console.log('enter pressed');
e.preventDefault();
var range = window.getSelection().getRangeAt(0);
var element = document.createElement('p');
// element.textContent = 'lorem'; // gets added in the right position
range.insertNode(element);
// range.setStart(element); // doesn't work
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id='content' contentEditable=true>test</p>
I need to get this working in Chrome for now.
How do I fix this?
A simple solution if you don't absolutely need the top contenteditable element to be a p element, is to add a contenteditable div as parent of your p element. Enter will automatically add p elements.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='content' contentEditable=true><p>test</p></div>
Related
I am really struggling with this.
I have two div on a page. The first one has got contents (mainly text). On the second div, I want to display the content of first div based on the position. for example if i select line 30, then the content of that line will be displayed in second div. Is there any idea to do that?
Thank you
This answer assumes you only want to copy the selected text to the new div and provices a basic idea how you can do so
In order to achieve that kind of behaviour you need to listen to the mouseup event in your container where you want text to be selected. That way we assume, that the user was selecting something and ended the selection.
I have prepared this JS fiddle for you: https://jsfiddle.net/djzkcw0m/
Code for the prove of concept:
HTML
Highlight some text with mouse in this container:
<div id="test">
This is some text and you can highlight it with your mouse
</div>
Result div:
<div id="result"></div>
JS
const testDiv = document.getElementById('test');
const resultDiv = document.getElementById('result');
function getSelectionText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
return text;
}
testDiv.addEventListener('mouseup', function(e) {
const selectedText = getSelectionText();
resultDiv.textContent = selectedText;
})
note
Method "getSelectionText()" is found from a related question Get the Highlighted/Selected text
I am trying to insert some HTML and some text in a contenteditable.
However, when I'm in this configuration:
Some text <span>A span tag</span>
If I have my caret at the end of the text and try to insert some text like this:
document.execCommand('insertHTML', false, ' my text');
Instead of getting what I expect:
Some text <span>A span tag</span> my text
I get this:
Some text <span>A span tag my text</span>
How can I force insertHTML to insert at the highest level?
You basically need to set the carretPosition at the end of the selection and then insert your html, I made a small snippet demonstrating this behavior
One you set the range based on a node, you can then choose to selection.collapseToEnd() like so:
currentSelection.collapseToEnd();
The snippet uses the mousedown event to make sure the focus stays on the contenteditable item
document.querySelector('#addContentButton').addEventListener('mousedown', (e) => {
var currentSelection = window.getSelection();
// make sure there is a range available
if (currentSelection.rangeCount > 0) {
// get the container from where it started
var target = currentSelection.getRangeAt(0).startContainer;
var range = document.createRange();
// check if it has any childNodes, if so take the last node otherwise choose the container itself
var targetNode = target.childNodes && target.childNodes.length ? target.childNodes[target.childNodes.length - 1] : target;
// select the node
range.selectNode( targetNode );
// remove all ranges from the window
currentSelection.removeAllRanges();
// add the new range
currentSelection.addRange( range );
// set the position of the carret at the end
currentSelection.collapseToEnd();
// focus the element if posible
target.focus && target.focus();
// insert the html at the end
document.execCommand('insertHTML', false, 'test');
}
e.preventDefault();
});
document.querySelector('#insertCurrentPosition').addEventListener('mousedown', (e) => {
var currentSelection = window.getSelection();
// make sure there is a range available
if (currentSelection.rangeCount > 0) {
// get the container from where it started
var target = currentSelection.getRangeAt(0).startContainer;
// focus the element if posible
target.focus && target.focus();
// insert the html at the end
document.execCommand('insertHTML', false, 'test');
}
e.preventDefault();
});
span {
margin: 0 5px;
}
<div contenteditable="true">This is <span>my span</span><span>and another one</span></div>
<button id="addContentButton">Append at end of editable element</button>
<button id="insertCurrentPosition">Insert at current position</button>
Not sure I 100% understand your question / problem, but inserting text into a span element is as simple as this:
HTML:
<span id="mySpan"></span>
JavaScript:
document.getElementById('mySpan').innerHTML = "Text to put inside span";
'contenteditable' simple returns a true/false indicating whether an object can or cannot be edited.
Hope this helped
I am dynamically generating <p> tags inside of a <div contenteditable=true> but the event handler I have setup to catch the keyup events coming from the <p> tags is not catching them.
HTML
<!-- Non-dynamically generated paragraph -->
<div class="note">
<p contenteditable="true"></p>
</div>
<!-- Contains dynamically generated paragraphs -->
<div class="note" contenteditable="true"></div>
<!-- Debug output -->
<div id="output"></div>
JS
$(document).ready(function() {
$(".note").keyup(function(evt) {
$("#output").append("<br/>note tag keyup");
// Surround paragraphs with paragraph tag
$(this).contents().filter(function() {
return this.nodeType === 3;
}).wrap('<p class="test"></p>');
});
$(".note").on("keyup", "p", function(evt) {
$("#output").append("<br/>p tag keyup");
});
});
Fiddle: https://jsfiddle.net/mibacode/axgccwoq/3/
The first div element demonstrates that I can successfully catch the keyup event from a paragraph tag generated on load. The second div shows that my dynamically generated paragraphs are not firing (or JQuery just can't catch) the keyup event.
Edit:
The issue/bug appears to be with how the paragraph tags are being generated and added to the DOM in this portion of the code:
$(this).contents().filter(function() {
return this.nodeType === 3;
}).wrap('<p class="test"></p>');
I believe for some the <p> tag isn't being added properly so the DOM isn't recognizing it or doesn't know it exists.
Edit 2:
I replaced the jQuery functionality which inserts the new paragraph tags with vanilla JS in hopes that might solve my issue, however it did not.
New code:
var textNode = null;
var nodes = this.childNodes;
for (var i = 0; i < nodes.length; i++)
{
if (nodes[i].nodeType === 3)
{
textNode = nodes[i];
break;
}
}
if (textNode)
{
var p = document.createElement("P");
var attr = document.createAttribute("contenteditable");
attr.value = "true";
p.setAttributeNode(attr);
p.addEventListener("keyup", function() {
$("#output").append("<br/>Special paragraph click!");
});
p.innerHTML = textNode.nodeValue;
var parent = $(this)[0];
parent.insertBefore(p, textNode);
parent.removeChild(textNode);
}
It appears it has to do with the way that JS handles events fired from elements within contenteditable elements.
The only problem is that you don't have the contenteditable attribute on the dynamic <p> tags. Just because they are contained within a contenteditable element doesn't make them editable also - they need the attribute explicitly.
In my updated fiddle, I have simplified it just to show that the dynamic tags work.
I am building a simplistic and easy-to-use text editor in Javascript, basically a WYSIWYG editor. I will be using the contenteditable attribute to the main wrapper (.wrapper). When you press enter inside the .wrapper, a new <p> element with a unique id gets appended to the wrapper.
I need a way to fetch which child element of the .wrapper that is currently selected (i.e., being focused or having the caret/text marker inside of it).
I've searched for days without any results, and I've tried using document.elementFromPoint() but without any proper results.
When using $(":focus"), I get the entire .wrapper and not the specific child element.
Edit:
Example HTML structure:
<div class="container t-wrapper" contenteditable>
</div>
Example Javascript code:
$(document).ready(function() {
$currentElement = $("[contenteditable]");
$(".t-wrapper").each(function() {
var id = generateIdentifier(6); // Ignore this
/* This creates the initial <p> child element, where you start typing. */
$(this).html('<p class="t-empty t-first" id="' + id + '"></p>');
$(this).on("mouseup", function() {
/* $currentElement = whatever element the caret is inside. */
});
$(this).on("keyup", function() {
/* $currentElement = whatever element the caret is inside. */
});
));
});
Edit 2:
I managed to get it fairly working with the mouseup event, since you're actually clicking on something. But I need this to work when moving the caret using the keyboard. Alternatively, I need some way to get the position of the caret in pixels, and then use document.elementFromPoint() to get the specific element.
:focus doesn't select your elements because they are not focusable.
You can make them focusable by adding tabindex="-1" in HTML, or tabIndex = -1 in JS.
var generateIdentifier = Math.random;
var currentElement = document.querySelector("[contenteditable]");
[].forEach.call(document.querySelectorAll(".t-wrapper"), function(el) {
var first = document.createElement('p');
first.className = "t-empty t-first";
first.id = generateIdentifier(6);
first.textContent = 'Press enter to create new paragraphs';
first.tabIndex = -1;
el.appendChild(first);
});
.container > :focus {
border: 1px solid blue;
}
<div class="container t-wrapper" contenteditable></div>
It seems that if you add it to the first paragraph, new paragraphs obtain it automatically. But if you want to be sure, I guess you could use a mutation observer or a keyup event listener to detect paragraphs without tabindex, and add it:
el.addEventListener("keyup", function(e) {
var newChild = el.querySelector('p:not([tabindex])');
if(newChild) newChild.tabIndex = -1;
});
document.activeElement will return the currently focused element.
I want to set the focus to a b tag (<b>[focus should be here]</b>).
My expected result was that the b tag into the div has the focus and if I would write, that the characters are bold.
Is this impossible? How can I do this?
Idea was from here:
focus an element created on the fly
HTML:
<div id="editor" class="editor" contentEditable="true">Hallo</div>
JS onDomready:
var input = document.createElement("b"); //create it
document.getElementById('editor').appendChild(input); //append it
input.focus(); //focus it
My Solution thanks to A1rPun:
add: 'input.tabIndex = 1;' and listen for the follow keys.
HTML:
<h1>You can start typing</h1>
<div id="editor" class="editor" contentEditable="true">Hallo</div>
JS
window.onload = function() {
var input = document.createElement("b"); //create it
document.getElementById('editor').appendChild(input); //append it
input.tabIndex = 1;
input.focus();
var addKeyEvent = function(e) {
//console.log('add Key');
var key = e.which || e.keyCode;
this.innerHTML += String.fromCharCode(key);
};
var addLeaveEvent = function(e) {
//console.log('blur');
// remove the 'addKeyEvent' handler
e.target.removeEventListener('keydown', addKeyEvent);
// remove this handler
e.target.removeEventListener(e.type, arguments.callee);
};
input.addEventListener('keypress', addKeyEvent);
input.addEventListener('blur', addLeaveEvent);
};
You can add a tabIndex property to allow the element to be focused.
input.tabIndex = 1;
input.focus();//now you can set the focus
jsfiddle
Edit:
I think the best way to solve your problem is to style an input tag with font-weight: bold.
I had to cheat a little by adding an empty space inside the bold area because I couldn't get it to work on the empty element.
This works by moving the selector inside the last element in the contentEditable since the bold element is the last one added.
It can be edited to work on putting the focus on any element.
http://jsfiddle.net/dnzajx21/3/
function appendB(){
var bold = document.createElement("b");
bold.innerHTML = " ";
//create it
document.getElementById('editor').appendChild(bold); //append it
setFocus();
}
function setFocus() {
var el = document.getElementById("editor");
var range = document.createRange();
var sel = window.getSelection();
range.setStartAfter(el.lastChild);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
el.focus();
}
The SetFocus function I took was from this question: How to set caret(cursor) position in contenteditable element (div)?