Is it possible within the following Fiddle, to have a feature where all dynamic paragraphs <p>generated can be dragged by the user dynamically, while the text in each generated paragraph <p> still maintains its text attribute with the text flowing into each paragraph <p> when resizing?
Fiddle
$(function() {
$('select').on('change', function() {
//Lets target the parent element, instead of P. P will inherit it's font size (css)
var targets = $('#content'),
property = this.dataset.property;
targets.css(property, this.value);
sameheight('#content p');
}).prop('selectedIndex', 0);
});
var btn = document.getElementById('go'),
textarea = document.getElementById('textarea1'),
content = document.getElementById('content'),
chunkSize = 100;
btn.addEventListener('click', initialDistribute);
content.addEventListener('keyup', handleKey);
content.addEventListener('paste', handlePaste);
function initialDistribute() {
var text = textarea.value;
while (content.hasChildNodes()) {
content.removeChild(content.lastChild);
}
rearrange(text);
}
function rearrange(text) {
var chunks = splitText(text, false);
chunks.forEach(function(str, idx) {
para = document.createElement('P');
para.setAttribute('contenteditable', true);
para.textContent = str;
content.appendChild(para);
});
sameheight('#content p');
}
function handleKey(e) {
var para = e.target,
position,
key, fragment, overflow, remainingText;
key = e.which || e.keyCode || 0;
if (para.tagName != 'P') {
return;
}
if (key != 13 && key != 8) {
redistributeAuto(para);
return;
}
position = window.getSelection().getRangeAt(0).startOffset;
if (key == 13) {
fragment = para.lastChild;
overflow = fragment.textContent;
fragment.parentNode.removeChild(fragment);
remainingText = overflow + removeSiblings(para, false);
rearrange(remainingText);
}
if (key == 8 && para.previousElementSibling && position == 0) {
fragment = para.previousElementSibling;
remainingText = removeSiblings(fragment, true);
rearrange(remainingText);
}
}
function handlePaste(e) {
if (e.target.tagName != 'P') {
return;
}
overflow = e.target.textContent + removeSiblings(fragment, true);
rearrange(remainingText);
}
function redistributeAuto(para) {
var text = para.textContent,
fullText;
if (text.length > chunkSize) {
fullText = removeSiblings(para, true);
}
rearrange(fullText);
}
function removeSiblings(elem, includeCurrent) {
var text = '',
next;
if (includeCurrent && !elem.previousElementSibling) {
parent = elem.parentNode;
text = parent.textContent;
while (parent.hasChildNodes()) {
parent.removeChild(parent.lastChild);
}
} else {
elem = includeCurrent ? elem.previousElementSibling : elem;
while (next = elem.nextSibling) {
text += next.textContent;
elem.parentNode.removeChild(next);
}
}
return text;
}
function splitText(text, useRegex) {
var chunks = [],
i, textSize, boundary = 0;
if (useRegex) {
var regex = new RegExp('.{1,' + chunkSize + '}\\b', 'g');
chunks = text.match(regex) || [];
} else {
for (i = 0, textSize = text.length; i < textSize; i = boundary) {
boundary = i + chunkSize;
if (boundary <= textSize && text.charAt(boundary) == ' ') {
chunks.push(text.substring(i, boundary));
} else {
while (boundary <= textSize && text.charAt(boundary) != ' ') {
boundary++;
}
chunks.push(text.substring(i, boundary));
}
}
}
return chunks;
}
function sameheight(selector){
var max_y=0;
var y=0;
$(selector).css('height','');
$(selector).each(function(){
y=$(this).outerHeight();
if(y>max_y) max_y=y;
});
$(selector).css('height',max_y);
}
#text_land {
border: 1px solid #ccc;
padding: 25px;
margin-bottom: 30px;
}
textarea {
width: 95%;
}
label {
display: block;
width: 50%;
clear: both;
margin: 0 0 .5em;
}
label select {
width: 50%;
float: right;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
font-family: monospace;
font-size: 1em;
}
h3 {
margin: 1.2em 0;
}
div {
margin: 1.2em;
}
textarea {
width: 100%;
}
button {
padding: .5em;
}
p {
/*Here the sliles for OTHER paragraphs*/
}
#content p {
font-size:inherit;/*So it gets the font size set on the #content div*/
padding: 1.2em .5em;
margin: 1.4em 0;
border: 1px dashed #aaa;
overflow:hidden;
}
p {width:400px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="styles">
<label>Font-size:
<select data-property="font-size">
<option disabled>
Select font-size:
</option>
<option>
smaller
</option>
<option>
10px
</option>
<option>
12px
</option>
<option>
14px
</option>
<option>
16px
</option>
<option>
18px
</option>
<option>
20px
</option>
<option>
larger
</option>
</select>
</label>
</div>
<div>
<h3>Paste text in the field below to divide text into paragraphs..</h3>
<textarea id="textarea1" placeholder="Type text here, then press the button below." rows="5">
</textarea>
<br>
<br>
<button id="go">Divide Text into Paragraphs</button>
</div>
<div>
<h3 align="right">Divided Text Will Appear Below:</h3>
<hr>
<div id="content"></div>
</div>
For the dragging purpose you can use jquery ui droppable : http://jqueryui.com/droppable/
Related
I am working on a autocomplete feature and to populate in on a list currently the list is just showing the results but I can't select them to add it to the input element.
Here is a sample of the code:
var search_terms = ['a', 'b', 'c'];
function autocompleteMatch(input) {
if (input == '') {
return [];
}
var reg = new RegExp(input);
return search_terms.filter(function(term) {
if (term.match(reg)) {
return term;
}
});
}
function showResults(val) {
res = document.getElementById("result");
res.innerHTML = '';
let list = '';
let terms = autocompleteMatch(val);
for (i = 0; i < terms.length; i++) {
list += '<li>' + terms[i] + '</li>';
}
res.innerHTML = '<ul>' + list + '</ul>';
}
<input type="text" class="form-control" placeholder="Explain in fewer words with the primary product key word (Eg: OneDrive sync issue, etc.)" name="post" required id="id_post" onKeyUp="showResults(this.value)">
<span class="fas fa-asterisk" style="font-size:12px;color:red;position:absolute; right:20px;top:12px;" id="asterix"></span>
<div id="result"></div>
Any advice to add the elements on list to the input. I've search for similar suggestions but I couldn't apply the answers to my code.
Edit: In my case the solutions posted here were not working because the calling of the script
<script type="text/javascript" src="app.js" charset="utf-8"></script>
was at the top. So it was failing with a Uncaught TypeError: Cannot read property 'addEventListener' of null. After moving <script> section to the end of the <body> suggested answers started to work.
Is this how it should work. I added an event listener to res that tests id a <li> was clicked. If so, the innerHTML of the <li> is inserted as value in the <input>. Using the dispatchEvent() I update the list as if it was a keyup event.
var search_terms = ['abc', 'abcde', 'abde'];
var res = document.getElementById("result");
var id_post = document.getElementById("id_post");
function autocompleteMatch(input) {
if (input == '') {
return [];
}
var reg = new RegExp(input)
return search_terms.filter(function(term) {
if (term.match(reg)) {
return term;
}
});
}
function showResults(val) {
let terms = autocompleteMatch(val);
list = terms.map(term => `<li>${term}</li>`).join('');
res.innerHTML = '<ul>' + list + '</ul>';
}
res.addEventListener('click', e => {
if(e.target.nodeName == "LI"){
id_post.value = e.target.innerHTML;
id_post.dispatchEvent(new Event('keyup'));
}
});
<input type="text" class="form-control" placeholder="Explain in fewer words with the primary product key word (Eg: OneDrive sync issue, etc.)" name="post" required id="id_post" onKeyUp="showResults(this.value)">
<div id="result"></div>
Considering your original code there are some missing details like autocomplete function and keyboard events (up/down/enter).
1) HTML
// form (autocomplete off) disables autocomplete integration with external tools like 1password
<form autocomplete="off">
<div class="autocomplete" id="autocomplete-container">
<input id="autocomplete-input" type="text" placeholder="Type Something...">
</div>
<input type="submit">
</form>
2) List of possible options (to make it dynamic, load the list before binding it to the autocomplete method)
const data = ["Aaa", "Aab", "Aac", "Abc", "Bbc", "Bbd", "Xpto", "Item1", "Item2", "SomethingElse"];
3) Autocomplete Functionality
const autocomplete = (container, inputElement, list) => {
var currentFocus = -1;
inputElement.addEventListener('input', () => {
var autocompleteText = inputElement.value;
hideList();
if (!autocompleteText) {
return false;
}
const autocompleteList = document.createElement('div');
autocompleteList.setAttribute('id', 'autocomplete-list');
autocompleteList.setAttribute('class', 'autocomplete-items');
container.appendChild(autocompleteList);
list.forEach((item, index) => {
if (
item.substr(0, autocompleteText.length).toUpperCase() ===
autocompleteText.toUpperCase()
) {
const tempId = `hiddenInput_${index}`;
const text = item.substr(0, autocompleteText.length);
const autocompleteMatch = document.createElement('div');
autocompleteMatch.innerHTML = `<strong>${text}</strong>`;
autocompleteMatch.innerHTML += item.substr(autocompleteText.length);
autocompleteMatch.innerHTML += `<input type='hidden' id='${tempId}' value='${item}'>`;
autocompleteMatch.addEventListener('click', (event) => {
const clickedElement = event.target.getElementsByTagName('input')[0];
inputElement.value = clickedElement.value;
hideList();
});
autocompleteList.appendChild(autocompleteMatch);
}
});
});
inputElement.addEventListener('keydown', function (e) {
const autoCompleteList = document.getElementById('autocomplete-list');
var autoCompleteDiv;
if (autoCompleteList) {
autoCompleteDiv = autoCompleteList.getElementsByTagName('div');
}
if (e.keyCode === 40) {
// KEY DOWN
currentFocus++;
addActive(autoCompleteDiv);
} else if (e.keyCode === 38) {
// KEY UP
currentFocus--;
addActive(autoCompleteDiv);
} else if (e.keyCode === 13) {
// ENTER
e.preventDefault();
if (currentFocus > -1 && autoCompleteDiv) {
autoCompleteDiv[currentFocus].click();
}
}
});
const addActive = (item) => {
if (!item) {
return false;
}
removeActive(item);
if (currentFocus >= item.length) {
currentFocus = 0;
}
if (currentFocus < 0) {
currentFocus = item.length - 1;
}
item[currentFocus].classList.add('autocomplete-active');
};
const removeActive = (autoCompleteItems) => {
Array.from(autoCompleteItems).forEach((item) => {
item.classList.remove('autocomplete-active');
});
};
const hideList = (element) => {
var autoCompleteItems =
document.getElementsByClassName('autocomplete-items');
if (autoCompleteItems && autoCompleteItems.length > 0) {
Array.from(autoCompleteItems).forEach((item) => {
if (element !== item && element !== inputElement) {
item.parentNode.removeChild(item);
}
});
}
};
document.addEventListener('click', (event) => hideList(event.target));
};
// this part binds the autocomplete with the HTML
window.addEventListener('load', function () {
const container = document.getElementById('autocomplete-container');
const inputElement = document.getElementById('autocomplete-input');
autocomplete(container, inputElement, data);
});
CSS
* { box-sizing: border-box; }
body {
font: 16px Arial;
}
.autocomplete {
display: inline-block;
position: relative;
width: 300px;
}
input {
border: 1px solid transparent;
background-color: #f1f1f1;
padding: 10px;
font-size: 16px;
}
input[type=text] {
background-color: #f1f1f1;
width: 100%;
}
input[type=submit] {
background-color: DodgerBlue;
color: #fff;
}
.autocomplete-items {
position: absolute;
border: 1px solid #d4d4d4;
border-bottom: none;
border-top: none;
z-index: 99;
top: 100%;
left: 0;
right: 0;
}
.autocomplete-items div {
padding: 10px;
cursor: pointer;
background-color: #fff;
border-bottom: 1px solid #d4d4d4;
}
.autocomplete-items div:hover {
background-color: #e9e9e9;
}
.autocomplete-active {
background-color: DodgerBlue !important;
color: #ffffff;
}
Based on W3School - How TO - Autocomplete
Live Version - Codesandbox
Try This Code
var searchTerms = ["OneDrive sync issue", "Abcde", "123456"];
var result = document.getElementById("result");
rul = document.createElement("ul");
result.appendChild(rul);
rul.classList.add("datalist");
for(var x = 0; x < searchTerms.length; x++ ){
rli = document.createElement("li");
rul.appendChild(rli);
rli.innerHTML = searchTerms[x];
rli.classList.add("d-none");
rli.addEventListener("click", function(){
document.getElementById("id_post").value = this.innerHTML;
this.classList.add("d-none");
});
}
function showResults(v){
var ListItems = rul.querySelectorAll("li");
for(var i = 0; i < ListItems.length; i++){
var c = ListItems[i].innerHTML.toLowerCase();
v = v.toLowerCase();
if(c.indexOf(v) > -1 && v !== ""){
ListItems[i].classList.remove("d-none");
}
else{
ListItems[i].classList.add("d-none");
}
}
}
.d-none{ display:none; }
.datalist li{ cursor:pointer; }
<input
type="text"
class="form-control"
placeholder="Explain in fewer words with the primary product key word (Eg: OneDrive sync issue, etc.)"
name="post"
required
id="id_post"
onKeyUp="showResults(this.value)">
<span
class="fas fa-asterisk"
style="font-size:12px;color:red;position:absolute; right:20px;top:12px;"
id="asterix"></span>
<div id="result"></div>
I have been trying to figure out the issue with using regular expressions to ignore lines starting with double hyphens and count the single lines as separate and double hyphenated lines counted as separately and display outside the text area.
I have tried and got success with counting the each line but ignoring hyphens and counting I have no idea on how can I do that using regular expressions.
On another side, I want to append the code inside of a span Item buts it's removing the text of item element.
Here is my code and sample Image.
$(document).ready(function(){
var items = $('#items');
var groups = $('#groups');
$('#ingredients_list').keydown(function(e) {
newLines = $(this).val().split("\n").length;
items.text(newLines);
});
});
.ingredients__section {
padding: 20px;
width: 100%;
box-sizing: border-box;
}
.ingredients__section textarea {
width: 100%;
}
.ingredients__section h2 {
color:#0433a7;
margin-bottom: 20px;
}
.ingredients__header {
display: table;
width: 100%;
table-layout:fixed;
}
.ingredients__title { display: table-cell; }
.ingredients__countinfo { display: table-cell; text-align:right; }
.item-count,
.group-count { padding: 5px 15px; background-color:#e4ebef; margin-left: 5px; font-size: 14px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="ingredients__section">
<div class="ingredients__header">
<div class="ingredients__title"><h2>INGREDIENTS</h2></div>
<div class="ingredients__countinfo">
<span class="group-count" id="groups">Groups:</span>
<span class="item-count" id="items">Items:</span>
</div>
</div>
<form>
<textarea id="ingredients_list" rows="15"></textarea><br />
</form>
</div>
Initialize counters
var groupsCount = 0;
var itemsCount = 0;
Get array of entered text
var arrayOfItems = $(this).val().split("\n");
Run for all elements in array and check first two symbols. If it is -- then groupsCount++ else itemsCount++
for (var i=0; i<arrayOfItems.length; i++) {
if (arrayOfItems[i][0] === '-' && arrayOfItems[i][1] === '-') {
groupsCount += 1;
itemsCount -= 1;
groups.text("Groups: " + groupsCount);
} else {
itemsCount += 1;
items.text("Items: " + itemsCount);
}
}
$(document).ready(function(){
var items = $('#items');
var groups = $('#groups');
$('#ingredients_list').keypress(function(e) {
var groupsCount = 0;
var itemsCount = 0;
var arrayOfItems = $(this).val().split("\n");
for (var i=0; i<arrayOfItems.length; i++) {
if (arrayOfItems[i] != '') {
if (arrayOfItems[i][0] === '-' && arrayOfItems[i][1] === '-') {
groupsCount += 1;
groups.text("Groups: " + groupsCount);
} else {
itemsCount += 1;
items.text("Items: " + itemsCount);
}
} else {
groups.text("Groups: " + groupsCount);
items.text("Items: " + itemsCount);
}
}
});
$(document).mousedown(function (e) {
var groupsCount = 0;
var itemsCount = 0;
var arrayOfItems = $('#ingredients_list').val().split("\n");
for (var i=0; i<arrayOfItems.length; i++) {
if (arrayOfItems[i] != '') {
if (arrayOfItems[i][0] === '-' && arrayOfItems[i][1] === '-') {
groupsCount += 1;
groups.text("Groups: " + groupsCount);
} else {
itemsCount += 1;
items.text("Items: " + itemsCount);
}
} else {
groups.text("Groups: " + groupsCount);
items.text("Items: " + itemsCount);
}
}
});
});
.ingredients__section {
padding: 20px;
width: 100%;
box-sizing: border-box;
}
.ingredients__section textarea {
width: 100%;
}
.ingredients__section h2 {
color:#0433a7;
margin-bottom: 20px;
}
.ingredients__header {
display: table;
width: 100%;
table-layout:fixed;
}
.ingredients__title { display: table-cell; }
.ingredients__countinfo { display: table-cell; text-align:right; }
.item-count,
.group-count { padding: 5px 15px; background-color:#e4ebef; margin-left: 5px; font-size: 14px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="ingredients__section">
<div class="ingredients__header">
<div class="ingredients__title"><h2>INGREDIENTS</h2></div>
<div class="ingredients__countinfo">
<span class="group-count" id="groups">Groups:</span>
<span class="item-count" id="items">Items:</span>
</div>
</div>
<form>
<textarea id="ingredients_list" rows="15"></textarea><br />
</form>
</div>
Its good, But I have seen that if you type a word with double hyphens -- or without the same --, it is adding +1 to both groupItems and listItems which are not what you are looking for.
It means if you start group name with -- it should add +1 to groups instead of items, so here is the small change in your code that might help you to fix.
$(document).ready(function(){
var items = $('#items');
var groups = $('#groups');
$('#ingredients_list').keypress(function(e) {
var groupsCount = 0;
var itemsCount = 0;
var arrayOfItems = $(this).val().split("\n");
console.log(arrayOfItems);
for (var i=0; i<arrayOfItems.length; i++) {
if (arrayOfItems[i][0] === '-' && arrayOfItems[i][1] === '-') {
groupsCount += 1;
groups.text("Groups: " + groupsCount);
} if (arrayOfItems[i][0] === '.'){ // I have given '.' here in single quote, you can add regex what you are looking for like numbers, letters, or expressions that start with.
itemsCount += 1;
items.text("Items: " + itemsCount);
}
}
});
});
The following fiddle allows text to be imported into a <textarea> and dynamically generated into equal paragraphs. Is it possible to break the text in to paragraphs without breaking the text in the middle of a sentence? I want the length of each paragraph to be at or near the ChunkSize or user-adjusted limit, with each paragraph's element on the page being the same height.
If an updated fiddle could please be provided, would be extremely helpful, as I am still new to coding.
Thank You!
Fiddle
$(function() {
$('select').on('change', function() {
//Lets target the parent element, instead of P. P will inherit it's font size (css)
var targets = $('#content'),
property = this.dataset.property;
targets.css(property, this.value);
sameheight('#content p');
}).prop('selectedIndex', 0);
});
var btn = document.getElementById('go'),
textarea = document.getElementById('textarea1'),
content = document.getElementById('content');
chunkSize = 100;
btn.addEventListener('click', initialDistribute);
content.addEventListener('keyup', handleKey);
content.addEventListener('paste', handlePaste);
function initialDistribute() {
custom = parseInt(document.getElementById("custom").value);
chunkSize = (custom > 0) ? custom : chunkSize;
var text = textarea.value;
while (content.hasChildNodes()) {
content.removeChild(content.lastChild);
}
rearrange(text);
}
function rearrange(text) {
var chunks = splitText(text, false);
chunks.forEach(function(str, idx) {
para = document.createElement('P');
para.classList.add("Paragraph_CSS");
para.setAttribute('contenteditable', true);
para.textContent = str;
content.appendChild(para);
});
sameheight('#content p');
}
function handleKey(e) {
var para = e.target,
position,
key, fragment, overflow, remainingText;
key = e.which || e.keyCode || 0;
if (para.tagName != 'P') {
return;
}
if (key != 13 && key != 8) {
redistributeAuto(para);
return;
}
position = window.getSelection().getRangeAt(0).startOffset;
if (key == 13) {
fragment = para.lastChild;
overflow = fragment.textContent;
fragment.parentNode.removeChild(fragment);
remainingText = overflow + removeSiblings(para, false);
rearrange(remainingText);
}
if (key == 8 && para.previousElementSibling && position == 0) {
fragment = para.previousElementSibling;
remainingText = removeSiblings(fragment, true);
rearrange(remainingText);
}
}
function handlePaste(e) {
if (e.target.tagName != 'P') {
return;
}
overflow = e.target.textContent + removeSiblings(fragment, true);
rearrange(remainingText);
}
function redistributeAuto(para) {
var text = para.textContent,
fullText;
if (text.length > chunkSize) {
fullText = removeSiblings(para, true);
}
rearrange(fullText);
}
function removeSiblings(elem, includeCurrent) {
var text = '',
next;
if (includeCurrent && !elem.previousElementSibling) {
parent = elem.parentNode;
text = parent.textContent;
while (parent.hasChildNodes()) {
parent.removeChild(parent.lastChild);
}
} else {
elem = includeCurrent ? elem.previousElementSibling : elem;
while (next = elem.nextSibling) {
text += next.textContent;
elem.parentNode.removeChild(next);
}
}
return text;
}
function splitText(text, useRegex) {
var chunks = [],
i, textSize, boundary = 0;
if (useRegex) {
var regex = new RegExp('.{1,' + chunkSize + '}\\b', 'g');
chunks = text.match(regex) || [];
} else {
for (i = 0, textSize = text.length; i < textSize; i = boundary) {
boundary = i + chunkSize;
if (boundary <= textSize && text.charAt(boundary) == ' ') {
chunks.push(text.substring(i, boundary));
} else {
while (boundary <= textSize && text.charAt(boundary) != ' ') {
boundary++;
}
chunks.push(text.substring(i, boundary));
}
}
}
return chunks;
}
#text_land {
border: 1px solid #ccc;
padding: 25px;
margin-bottom: 30px;
}
textarea {
width: 95%;
}
label {
display: block;
width: 50%;
clear: both;
margin: 0 0 .5em;
}
label select {
width: 50%;
float: right;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
font-family: monospace;
font-size: 1em;
}
h3 {
margin: 1.2em 0;
}
div {
margin: 1.2em;
}
textarea {
width: 100%;
}
button {
padding: .5em;
}
p {
/*Here the sliles for OTHER paragraphs*/
}
#content p {
font-size: inherit;
/*So it gets the font size set on the #content div*/
padding: 1.2em .5em;
margin: 1.4em 0;
border: 1px dashed #aaa;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<h3>Import Text below, then press the button</h3>
<textarea id="textarea1" placeholder="Type text here, then press the button below." rows="5">
</textarea>
<input style="width:200px;" id="custom" placeholder="Custom Characters per box">
<br>
<button style="width:200px;" id="go">Divide Text into Paragraphs</button>
</div>
<div>
<h3 align="right">Divided Text Will Appear Below:</h3>
<hr>
<div id="content"></div>
</div>
You can take the approach of splitting the text in to sentences, and then adding sentences to the paragraphs until you reach the desired length (chunkSize in your code).
function splitText (text) {
var paragraph = "",
paragraphs = [],
sentenceRegex = /[^\.!\?]+([\.!\?]+|\s*$)/g,
sentences = text.match(sentenceRegex);
sentences.forEach(function createParagraphs (sentence, index) {
paragraph += sentence;
if (paragraph.length >= chunkSize || index === sentences.length - 1) {
paragraphs.push(paragraph);
paragraph = "";
}
});
return paragraphs.length === 0 ? [text] : paragraphs;
}
https://jsfiddle.net/DirectCtrl/95kuyw4g/4/ (Tried to keep the rest of the code as similar to what it was as possible).
This doesn't deal with margins (meaning you could potentially get much longer paragraphs if you have sentences which end near the boundaries or go well beyond the boundary limit), though those kinds of problems are very likely to appear regardless on edge cases (e.g. with a chunkSize of 100 characters, what do you do when the first sentence is 40 characters and the second is 160 characters?). Tweaking this to use a margin should be pretty trivial, though, if that is a requirement. As the number of characters per paragraph increases, this would become less of an issue.
The following JSFiddle splits texts into individual paragraph <p> boxes with a set text limit.
Is it possible to change the text size in the boxes according to the select option through contenteditable while still allowing to edit, removing the text limit and remaining the dynamic size property?
UPDATE 2: The boxes generated all need to be: "equal the same height and width" and change when the font is updated dynamically, needing to be consistent across all elements.
UPDATE 3: The last generated paragraph is not equal to the other paragraphs with border size height.
UPDATE 4: All paragraphs need to be generated equally with the height attribute auto. The problem with the current answers is the that the last generated paragraph border also needs to equal the same height as the previous height border as the other paragraphs, including when changing the font size.
Update 5 [image]: Example of Problem with last divided paragraph height and border not equal to others.
Link to Fiddle
If a new fiddle could be provided, it would be very much appreciated, as I am still new to coding.
Thank You!
$(function() {
$('select').on('change', function() {
var targets = $('p'),
property = this.dataset.property;
targets.css(property, this.value);
}).prop('selectedIndex', 0);
});
var btn = document.getElementById('go'),
textarea = document.getElementById('textarea1'),
content = document.getElementById('content'),
chunkSize = 100;
btn.addEventListener('click', initialDistribute);
content.addEventListener('keyup', handleKey);
content.addEventListener('paste', handlePaste);
function initialDistribute() {
var text = textarea.value;
while (content.hasChildNodes()) {
content.removeChild(content.lastChild);
}
rearrange(text);
}
function rearrange(text) {
var chunks = splitText(text, false);
chunks.forEach(function(str, idx) {
para = document.createElement('P');
para.setAttribute('contenteditable', true);
para.textContent = str;
content.appendChild(para);
});
}
function handleKey(e) {
var para = e.target,
position,
key, fragment, overflow, remainingText;
key = e.which || e.keyCode || 0;
if (para.tagName != 'P') {
return;
}
if (key != 13 && key != 8) {
redistributeAuto(para);
return;
}
position = window.getSelection().getRangeAt(0).startOffset;
if (key == 13) {
fragment = para.lastChild;
overflow = fragment.textContent;
fragment.parentNode.removeChild(fragment);
remainingText = overflow + removeSiblings(para, false);
rearrange(remainingText);
}
if (key == 8 && para.previousElementSibling && position == 0) {
fragment = para.previousElementSibling;
remainingText = removeSiblings(fragment, true);
rearrange(remainingText);
}
}
function handlePaste(e) {
if (e.target.tagName != 'P') {
return;
}
overflow = e.target.textContent + removeSiblings(fragment, true);
rearrange(remainingText);
}
function redistributeAuto(para) {
var text = para.textContent,
fullText;
if (text.length > chunkSize) {
fullText = removeSiblings(para, true);
}
rearrange(fullText);
}
function removeSiblings(elem, includeCurrent) {
var text = '',
next;
if (includeCurrent && !elem.previousElementSibling) {
parent = elem.parentNode;
text = parent.textContent;
while (parent.hasChildNodes()) {
parent.removeChild(parent.lastChild);
}
} else {
elem = includeCurrent ? elem.previousElementSibling : elem;
while (next = elem.nextSibling) {
text += next.textContent;
elem.parentNode.removeChild(next);
}
}
return text;
}
function splitText(text, useRegex) {
var chunks = [],
i, textSize, boundary = 0;
if (useRegex) {
var regex = new RegExp('.{1,' + chunkSize + '}\\b', 'g');
chunks = text.match(regex) || [];
} else {
for (i = 0, textSize = text.length; i < textSize; i = boundary) {
boundary = i + chunkSize;
if (boundary <= textSize && text.charAt(boundary) == ' ') {
chunks.push(text.substring(i, boundary));
} else {
while (boundary <= textSize && text.charAt(boundary) != ' ') {
boundary++;
}
chunks.push(text.substring(i, boundary));
}
}
}
return chunks;
}
#text_land {
border: 1px solid #ccc;
padding: 25px;
margin-bottom: 30px;
}
textarea {
width: 95%;
}
label {
display: block;
width: 50%;
clear: both;
margin: 0 0 .5em;
}
label select {
width: 50%;
float: right;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
font-family: monospace;
font-size: 1em;
}
h3 {
margin: 1.2em 0;
}
div {
margin: 1.2em;
}
textarea {
width: 100%;
}
button {
padding: .5em;
}
p {
padding: 1.2em .5em;
margin: 1.4em 0;
border: 1px dashed #aaa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="styles">
<label>Font-size:
<select data-property="font-size">
<option disabled>
Select font-size:
</option>
<option>
smaller
</option>
<option>
10px
</option>
<option>
12px
</option>
<option>
14px
</option>
<option>
16px
</option>
<option>
18px
</option>
<option>
20px
</option>
<option>
larger
</option>
</select>
</label>
</div>
<div>
<h3>Paste text in the field below to divide text into paragraphs..</h3>
<textarea id="textarea1" placeholder="Type text here, then press the button below." rows="5">
</textarea>
<br>
<br>
<button id="go">Divide Text into Paragraphs</button>
</div>
<div>
<h3 align="right">Divided Text Will Appear Below:</h3>
<hr>
<div id="content"></div>
</div>
Here you go JSFiddle
$('#FontSize').change(function(){
var fontsize = $(this).val();
$('textarea').css('fontSize',fontsize);
});
I'm not sure if I'm understanding the problem... You can set the font size to the parent element (#content{font-size:whatever}) and inherit it (#content p {font-size:inherit} ). If you set the font size in the parent if will apply to the the already added paragraphs AND the new ones when you add them.
(Changes in the fiddle: Selector in the change of the select, CSS selectors/properties for "p" and "#content p")
(Answer edited for "same height" paragraphs)
To get same height I added a sameheight(selector) function. I'm unsure about when you would like to trigger it, I've put it on select change and rearange(text). (Hope it helps. Fixed the height in function using outerHeight)
...and fiddle edited again: sameheight is also fired on window resize events.
function sameheight(selector){
var max_y=0;
var y=0;
$(selector).css('height','');
$(selector).each(function(){
y=$(this).height();
if(y>max_y) max_y=y;
});
$(selector).css('height',max_y);
}
$(function() {
$('select').on('change', function() {
//Lets target the parent element, instead of P. P will inherit it's font size (css)
var targets = $('#content'),
property = this.dataset.property;
targets.css(property, this.value);
sameheight('#content p');
}).prop('selectedIndex', 0);
});
$( window ).resize(function() {
sameheight('#content p');
});
var btn = document.getElementById('go'),
textarea = document.getElementById('textarea1'),
content = document.getElementById('content'),
chunkSize = 100;
btn.addEventListener('click', initialDistribute);
content.addEventListener('keyup', handleKey);
content.addEventListener('paste', handlePaste);
function initialDistribute() {
var text = textarea.value;
while (content.hasChildNodes()) {
content.removeChild(content.lastChild);
}
rearrange(text);
}
function rearrange(text) {
var chunks = splitText(text, false);
chunks.forEach(function(str, idx) {
para = document.createElement('P');
para.setAttribute('contenteditable', true);
para.textContent = str;
content.appendChild(para);
});
sameheight('#content p');
}
function handleKey(e) {
var para = e.target,
position,
key, fragment, overflow, remainingText;
key = e.which || e.keyCode || 0;
if (para.tagName != 'P') {
return;
}
if (key != 13 && key != 8) {
redistributeAuto(para);
return;
}
position = window.getSelection().getRangeAt(0).startOffset;
if (key == 13) {
fragment = para.lastChild;
overflow = fragment.textContent;
fragment.parentNode.removeChild(fragment);
remainingText = overflow + removeSiblings(para, false);
rearrange(remainingText);
}
if (key == 8 && para.previousElementSibling && position == 0) {
fragment = para.previousElementSibling;
remainingText = removeSiblings(fragment, true);
rearrange(remainingText);
}
}
function handlePaste(e) {
if (e.target.tagName != 'P') {
return;
}
overflow = e.target.textContent + removeSiblings(fragment, true);
rearrange(remainingText);
}
function redistributeAuto(para) {
var text = para.textContent,
fullText;
if (text.length > chunkSize) {
fullText = removeSiblings(para, true);
}
rearrange(fullText);
}
function removeSiblings(elem, includeCurrent) {
var text = '',
next;
if (includeCurrent && !elem.previousElementSibling) {
parent = elem.parentNode;
text = parent.textContent;
while (parent.hasChildNodes()) {
parent.removeChild(parent.lastChild);
}
} else {
elem = includeCurrent ? elem.previousElementSibling : elem;
while (next = elem.nextSibling) {
text += next.textContent;
elem.parentNode.removeChild(next);
}
}
return text;
}
function splitText(text, useRegex) {
var chunks = [],
i, textSize, boundary = 0;
if (useRegex) {
var regex = new RegExp('.{1,' + chunkSize + '}\\b', 'g');
chunks = text.match(regex) || [];
} else {
for (i = 0, textSize = text.length; i < textSize; i = boundary) {
boundary = i + chunkSize;
if (boundary <= textSize && text.charAt(boundary) == ' ') {
chunks.push(text.substring(i, boundary));
} else {
while (boundary <= textSize && text.charAt(boundary) != ' ') {
boundary++;
}
chunks.push(text.substring(i, boundary));
}
}
}
return chunks;
}
function sameheight(selector){
var max_y=0;
var y=0;
$(selector).css('height','');
$(selector).each(function(){
y=$(this).outerHeight();
if(y>max_y) max_y=y;
});
$(selector).css('height',max_y);
}
#text_land {
border: 1px solid #ccc;
padding: 25px;
margin-bottom: 30px;
}
textarea {
width: 95%;
}
label {
display: block;
width: 50%;
clear: both;
margin: 0 0 .5em;
}
label select {
width: 50%;
float: right;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
font-family: monospace;
font-size: 1em;
}
h3 {
margin: 1.2em 0;
}
div {
margin: 1.2em;
}
textarea {
width: 100%;
}
button {
padding: .5em;
}
p {
/*Here the sliles for OTHER paragraphs*/
}
#content p {
font-size:inherit;/*So it gets the font size set on the #content div*/
padding: 1.2em .5em;
margin: 1.4em 0;
border: 1px dashed #aaa;
overflow:hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="styles">
<label>Font-size:
<select data-property="font-size">
<option disabled>
Select font-size:
</option>
<option>
smaller
</option>
<option>
10px
</option>
<option>
12px
</option>
<option>
14px
</option>
<option>
16px
</option>
<option>
18px
</option>
<option>
20px
</option>
<option>
larger
</option>
</select>
</label>
</div>
<div>
<h3>Paste text in the field below to divide text into paragraphs..</h3>
<textarea id="textarea1" placeholder="Type text here, then press the button below." rows="5">
</textarea>
<br>
<br>
<button id="go">Divide Text into Paragraphs</button>
</div>
<div>
<h3 align="right">Divided Text Will Appear Below:</h3>
<hr>
<div id="content"></div>
</div>
Try like this
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="styles">
<label>Font-size: <select data-property="font-size"
onchange="$('#textarea1').css('font-size',this.value)">
<option disabled>Select font-size:</option>
<option>smaller</option>
<option>10px</option>
<option>12px</option>
<option>14px</option>
<option>16px</option>
<option>18px</option>
<option>20px</option>
<option>larger</option>
</select></label>
</div>
<div>
<h3>Paste text in the field below to divide text into paragraphs..</h3>
<textarea id="textarea1" placeholder=
"Type text here, then press the button below." rows="5"> Test text
</textarea><br>
<br>
<button id="go">Divide Text into Paragraphs</button>
</div>
I believe i have the answer for you:
$(function() {
$('select').on('change', function() {
var targets = $('p'),
property = this.dataset.property;
$("#content").css({'font-size': this.value});
}).prop('selectedIndex', 0);
});
I changed the function in order to set the font-size to the div rather that the paragraph. Is this what you wanted? As it is what i gathered from the info provided.
https://jsfiddle.net/n9b6wju8/14/
You can inject a dynamic style element into the DOM and update it with the font size you're after. You could use an MV* framework to update its content.
$('#FontSize').change(function(){
var fontsize = $(this).val();
$('#style').remove(); //yes, there are better ways to update its content
$('head').append('<style id="style">#content { font-size: '+fontsize + '}</style>');
});
Fiddled: https://jsfiddle.net/ovfiddle/m75gre8o/1/
This should help:
$('#FontSize').change(function(){
var fontsize = $(this).val();
$('textarea').css('fontSize',fontsize);
});
To change of font size of the div and text area:
$(function() {
$('select').change(function() {
var fontsize = $(this).val();
$('#textarea1').css('fontSize',fontsize);
$('#content').css('fontSize',fontsize);
}).prop('selectedIndex', 0);
});
to keep the height of your text area: in CSS
textarea {
width: 95%;
height: 200px;
}
use px aslo instead em
I have created a fork of your fiddle and have edited(added) the event handler of select. I have added some code in their which maintains the font-size of editable p elements. Using that reference you can then apply all your styles as per your requirement.
Don't forget to mark it right and upvote if you find my answer right.
JS Fiddle Fork
targets.parent().find('style').remove();
$('<style>[contenteditable="true"]
{'+property+':'+this.value+'}\n[contenteditable="true"]:focus{'+property+':'+this.value+'}</style>').prependTo(targets.parent());
{'+property+':'+this.value+'}[contenteditable="true"]:focus{'+property+':'+this.value+'}</style>');
Try this
$('#FontSize').change(function(){
var fontsize = $(this).val();
$('textarea, #content p').css('fontSize',fontsize);
});
$('#go').click(function(){
var fontsize = $('#FontSize').val();
$('#content').css('fontSize',fontsize);
});
$(function() {
$('select').on('change', function() {
var targets = $('p'),
property = this.dataset.property;
targets.css(property, this.value);
}).prop('selectedIndex', 0);
});
var btn = document.getElementById('go'),
textarea = document.getElementById('textarea1'),
content = document.getElementById('content'),
chunkSize = 400;
btn.addEventListener('click', initialDistribute);
content.addEventListener('keyup', handleKey);
content.addEventListener('paste', handlePaste);
function initialDistribute() {
var text = textarea.value;
while (content.hasChildNodes()) {
content.removeChild(content.lastChild);
}
rearrange(text);
}
function rearrange(text) {
var chunks = splitText(text, false);
chunks.forEach(function(str, idx) {
para = document.createElement('P');
para.setAttribute('contenteditable', true);
para.textContent = str;
content.appendChild(para);
});
}
function handleKey(e) {
var para = e.target,
position,
key, fragment, overflow, remainingText;
key = e.which || e.keyCode || 0;
if (para.tagName != 'P') {
return;
}
if (key != 13 && key != 8) {
redistributeAuto(para);
return;
}
position = window.getSelection().getRangeAt(0).startOffset;
if (key == 13) {
fragment = para.lastChild;
overflow = fragment.textContent;
fragment.parentNode.removeChild(fragment);
remainingText = overflow + removeSiblings(para, false);
rearrange(remainingText);
}
if (key == 8 && para.previousElementSibling && position == 0) {
fragment = para.previousElementSibling;
remainingText = removeSiblings(fragment, true);
rearrange(remainingText);
}
}
function handlePaste(e) {
if (e.target.tagName != 'P') {
return;
}
overflow = e.target.textContent + removeSiblings(fragment, true);
rearrange(remainingText);
}
function redistributeAuto(para) {
var text = para.textContent,
fullText;
if (text.length > chunkSize) {
fullText = removeSiblings(para, true);
}
rearrange(fullText);
}
function removeSiblings(elem, includeCurrent) {
var text = '',
next;
if (includeCurrent && !elem.previousElementSibling) {
parent = elem.parentNode;
text = parent.textContent;
while (parent.hasChildNodes()) {
parent.removeChild(parent.lastChild);
}
} else {
elem = includeCurrent ? elem.previousElementSibling : elem;
while (next = elem.nextSibling) {
text += next.textContent;
elem.parentNode.removeChild(next);
}
}
return text;
}
function splitText(text, useRegex) {
var chunks = [],
i, textSize, boundary = 0;
if (useRegex) {
var regex = new RegExp('.{1,' + chunkSize + '}\\b', 'g');
chunks = text.match(regex) || [];
} else {
for (i = 0, textSize = text.length; i < textSize; i = boundary) {
boundary = i + chunkSize;
if (boundary <= textSize && text.charAt(boundary) == ' ') {
chunks.push(text.substring(i, boundary));
} else {
while (boundary <= textSize && text.charAt(boundary) != ' ') {
boundary++;
}
chunks.push(text.substring(i, boundary));
}
}
}
return chunks;
}
#text_land {
border: 1px solid #ccc;
padding: 25px;
margin-bottom: 30px;
}
textarea {
width: 95%;
height: 100px;
}
label {
display: block;
width: 50%;
clear: both;
margin: 0 0 .5em;
}
label select {
width: 50%;
float: right;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
font-family: monospace;
font-size: 1em;
}
h3 {
margin: 1.2em 0;
}
div {
margin: 1.2em;
}
textarea {
width: 100%;
}
button {
padding: .5em;
}
p {
padding: 1.2em .5em;
margin: 1.4em 0;
width: 200px;
height: 200px;
border: 1px dashed #aaa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<div id="styles">
<label>Font-size:
<select id="FontSize" data-property="font-size">
<option disabled>
Select font-size:
</option>
<option>
smaller
</option>
<option>
10px
</option>
<option>
12px
</option>
<option>
14px
</option>
<option>
16px
</option>
<option>
18px
</option>
<option>
20px
</option>
<option>
larger
</option>
</select></label>
</div>
<div>
<h3>Paste text in the field below to divide text into paragraphs..</h3>
<textarea id="textarea1" placeholder=
"Type text here, then press the button below." rows="5">
</textarea><br>
<br>
<button id="go">Divide Text into Paragraphs</button>
</div>
<div>
<h3 align="right">Divided Text Will Appear Below:</h3>
<hr>
<div id="content"></div>
</div>
I have a custom textarea. In this example, it makes the letters red or green, randomly.
var mydiv = document.getElementById('mydiv'),
myta = document.getElementById('myta');
function updateDiv() {
var fc;
while (fc = mydiv.firstChild) mydiv.removeChild(fc);
for (var i = 0; i < myta.value.length; i++) {
var span = document.createElement('span');
span.className = Math.random() < 0.5 ? 'green' : 'red';
span.appendChild(document.createTextNode(myta.value[i]));
mydiv.appendChild(span);
}
};
myta.addEventListener('input', updateDiv);
body { position: relative }
div, textarea {
-webkit-text-size-adjust: none;
width: 100%;
white-space: pre-wrap;
word-wrap: break-word;
overflow-wrap: break-word;
font: 1rem sans-serif;
padding: 2px;
margin: 0;
border-radius: 0;
border: 1px solid #000;
resize: none;
}
textarea {
position: absolute;
top: 0;
color: transparent;
background: transparent;
}
.red { color: #f00 }
.green { color: #0f0 }
<div id="mydiv"></div>
<textarea id="myta" autofocus=""></textarea>
There's an output div with a textarea over it. So the textarea doesn't cover up any of the colorful things below it, its color and background are set to transparent. Everything works here, except that the caret (the flashing cursor provided by the user agent) is transparent.
Is there a way to show the caret without making the textarea's text visible?
If I make the div above the textarea instead and give it pointer-events: none, the textarea is still visible underneath. This arrangements also makes smooth scrolling difficult, so it doesn't work for me.
Just insert your own caret!
function blink() {
document.getElementById('caret').hidden ^= 1;
blinkTimeout = setTimeout(blink, 500);
}
var mydiv = document.getElementById('mydiv'),
myta = document.getElementById('myta'),
blinkTimeout = setTimeout(blink, 500),
lastSelectionStart = 0,
lastSelectionEnd = 0,
whichSelection = true;
function updateDiv() {
var fc;
while (fc = mydiv.firstChild) mydiv.removeChild(fc);
if (myta.selectionStart != lastSelectionStart) {
lastSelectionStart = myta.selectionStart;
whichSelection = false;
}
if (myta.selectionEnd != lastSelectionEnd) {
lastSelectionEnd = myta.selectionEnd;
whichSelection = true;
}
var cursorPos = whichSelection ? myta.selectionEnd : myta.selectionStart;
for (var i = 0; i < myta.value.length; i++) {
if (i == cursorPos) {
var caret = document.createElement('span');
caret.id = 'caret';
caret.appendChild(document.createTextNode('\xA0'));
mydiv.appendChild(caret);
clearTimeout(blinkTimeout);
blinkTimeout = setTimeout(blink, 500);
}
var span = document.createElement('span');
span.className = Math.random() < 0.5 ? 'green' : 'red';
span.appendChild(document.createTextNode(myta.value[i]));
mydiv.appendChild(span);
}
if (myta.value.length == cursorPos) {
var caret = document.createElement('span');
caret.id = 'caret';
caret.appendChild(document.createTextNode('\xA0'));
mydiv.appendChild(caret);
clearTimeout(blinkTimeout);
blinkTimeout = setTimeout(blink, 500);
}
};
myta.addEventListener('input', updateDiv);
myta.addEventListener('focus', updateDiv);
myta.addEventListener('mousedown', function() {
setTimeout(updateDiv, 0);
});
myta.addEventListener('keydown', function() {
setTimeout(updateDiv, 0);
});
myta.addEventListener('blur', function() {
document.getElementById('caret').hidden = true;
clearTimeout(blinkTimeout);
});
body { position: relative }
div, textarea {
-webkit-text-size-adjust: none;
width: 100%;
white-space: pre-wrap;
word-wrap: break-word;
overflow-wrap: break-word;
font: 1rem sans-serif;
padding: 2px;
margin: 0;
border-radius: 0;
border: 1px solid #000;
resize: none;
}
textarea {
position: absolute;
top: 0;
color: transparent;
background: transparent;
}
.red { color: #f00 }
.green { color: #0f0 }
#caret {
display: inline-block;
position: absolute;
width: 1px;
background: #000;
}
#caret[hidden] { display: none }
<div id="mydiv"><span id="caret"> </span></div>
<textarea id="myta" autofocus=""></textarea>
I have here a <span> #caret inserted into the div which blinks every 500ms by toggling its hidden attribute using JS. To replicate browser behavior, I had to detect whether it was the selectionStart or the selectionEnd which the caret was actually at, and make it remain solid while text was being input.
This is a bit harder to achieve when the spans aren't of fixed length or are nested, but it's easier than fiddling with contentEditable with a more complex highlighter. This function will insert the caret in the right spot:
function insertNodeAtPosition(node, refNode, pos) {
if (typeof(refNode.nodeValue) == 'string') refNode.parentNode.insertBefore(node, refNode.splitText(pos));
else {
for (var i = 0; i < refNode.childNodes.length; i++) {
var chNode = refNode.childNodes[i];
if (chNode.textContent.length <= pos && i != refNode.childNodes.length - 1) pos -= chNode.textContent.length;
else return insertNodeAtPosition(node, chNode, pos);
}
}
}
Usage (where i is the position to insert it):
var caret = document.createElement('span');
caret.id = 'caret';
caret.appendChild(document.createTextNode('\xA0'));
insertNodeAtPosition(caret, mydiv, i);
clearTimeout(blinkTimeout);
blinkTimeout = setTimeout(blink, 500);
Why not simply use a <div contenteditable="true"></div> instead <textarea></textarea>?. With this you don't need the extra textarea. See a demo here.
HTML:
<div id="myta" autofocus="" contenteditable="true"></div>
JavaScript:
var myta = document.getElementById('myta');
function updateDiv() {
var fc;
var text = myta.innerText || myta.textContent;
while (fc = myta.firstChild) myta.removeChild(fc);
for (var i = 0; i < text.length; i++) {
var span = document.createElement('span');
span.className = Math.random() < 0.5 ? 'green' : 'red';
span.appendChild(document.createTextNode(text[i]));
myta.appendChild(span);
}
placeCaretAtEnd(myta);
};
myta.addEventListener('input', updateDiv);
Also, to move the caret at the end when you put the new text inside the div I used that function from this answer:
function placeCaretAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}