I am attempting to wrap every 20 chars in a new div container. I keep getting undefined I assume from the array results that do not exist yet. How can I wrap every 20 chars as the user types and reaches char 20, 40, 60,and 80 and place it into a div container with the max allowed chars being 80, and is there a better way of doing this than writing out the array groups manually by 20's.
function creaeTextField(clicked_id) {
var dropArea = document.getElementById('headarea');
var myArea = document.createElement('div');
var myAreaOuter = document.createElement('div');
myArea.className = "areaClass";
myAreaOuter.className = "areaClassOuter";
myArea.id = "areaClass";
myAreaOuter.id = "areaClassOuter";
myArea.contentEditable = "true";
if (clicked_id == 'text') {
myAreaOuter.appendChild(myArea);
dropArea.appendChild(myAreaOuter);
myArea.addEventListener("keydown", findLimitb);
myArea.addEventListener("keyup", findLimitb);
var style = window.getComputedStyle(myArea, null).getPropertyValue('font-size');
var fontSize = parseFloat(style);
function findLimitb() {
if (myArea.offsetHeight <= fontSize * 4) {
myArea.addEventListener("keydown", breaker);
} else {
if (event.keyCode === 8 || event.keyCode === 46 || event.keyCode === 37 || event.keyCode === 38 || event.keyCode === 39 || event.keyCode === 40) {
myArea.focus();
} else {
myArea.removeEventListener("keydown", breaker);
event.preventDefault();
myArea.style.height = fontSize * 4 + "px";
}
}
}
function breaker() {
var myAr = myArea.innerHTML.split("");
var divCon = document.createElement('div');
myArea.appendChild(divCon);
if (myArea.innerHTML.length > 20) {
divCon.innerHTML = myAr[0] + myAr[1] + myAr[2] + myAr[3] + myAr[4] + myAr[5] + myAr[6] + myAr[7] + myAr[8] + myAr[9] + myAr[10] + myAr[11] + myAr[12] + myAr[13] + myAr[14] + myAr[15] + myAr[16] + myAr[17] + myAr[18] + myAr[19] + myAr[20];
}
}
}
}
.headarea {
width: 100%;
height: 130px;
float: left;
}
.buttonStyle {
width: 60px;
height: 25px;
float: left;
}
.areaClassOuter {
float: left;
padding: 10px;
position: relative;
z-index: 29;
border: 1px #000000 solid;
}
.areaClass {
min-width: 100px;
max-width: 310px;
min-height: 60px;
max-height: 100px;
float: left;
padding: 7px;
position: relative;
z-index: 30;
border: 1px #000000 solid;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
font-size: 24pt;
text-align: center;
}
<div class="headarea" id="headarea"></div>
<button class="buttonStyle" id="text" type="button" onclick="creaeTextField(this.id)"></button>
You could just listen for the keydown-event like you did in your question and then work with substring to get and remove the first part of the string. The first part can then be appended to an container and the rest of the string (in most cases only 1 char) can be set as content of the input-element.
you should also consider using textContent instead of innerHtml, so that the user is able input safely characters that are used by html.
let textOutput = document.getElementById('textOutput');
let textInput = document.getElementById('textInput');
let maxInputLength = 20;
let maxOutputElement = 4; // 4*20 = 80 Chars
textInput.addEventListener('keydown', function(event) {
if (textOutput.children.length >= maxOutputElement) {
event.preventDefault();
textInput.textContent = '';
}
let textChanged = false;
let text = textInput.textContent;
while (text.length >= maxInputLength) {
let firstPart = text.substring(0, maxInputLength);
let textElement = document.createElement('div');
textElement.textContent = firstPart;
textElement.contentEditable = true;
textElement.addEventListener('keyup', function(event) {
let editText = event.currentTarget.textContent;
if (editText.length >= maxInputLength) {
event.currentTarget.textContent = editText.substring(0, maxInputLength);
}
});
textOutput.appendChild(textElement);
text = text.substring(maxInputLength);
textChanged = true;
}
if (textChanged) textInput.textContent = text;
});
#textOutput div {
display: inline-block;
margin: 5px;
background-color: #EEE;
}
#textInput {
display: block;
width: 160px;
height: 20px;
background: #EEE;
border: 1px dashed #CCC;
}
<div id="textOutput"></div>
<div id="textInput" contenteditable="true"></div>
You could split the string into spaces and use a modulus to see when 20 items have been reached.
This makes the code independent and allows you to change the number of words allowed in each string.
We split on the space and then iterate over the words, we reconstruct the string but push the results into an array when we reach the 'paragraphlength'. You then end up with an array of strings that are 20 words long.
There are some edge cases that aren't covered here and may not be an issue depending on your use case. For example, what if a user just puts illegible characters in, ect
//The wording as a string
const words = "this is a really long set of words that need to be broken down into a paragraphs array that has lots of words that need to be broken down into a nicer structure for users to easier consume. We split on the space and then iterate over the words, we reconstruct the string but push the results into an array when we reach the 'paragraphlength'";
const getParagraphs = (words, paragraphLength) => {
let paragraphs =[];
words.split(" ").reduce((memo, word, index) => {
if(index > 0 && index % paragraphLength === 0)
{
paragraphs.push(memo);
memo = "";
}
return memo + " " + word;
}, "");
return paragraphs
}
const paragraphs = getParagraphs(words, 20);
console.log("original string ===>", words);
console.log("paragraphs array ===>", paragraphs);
//below is for display purposes only
let results = document.getElementById('results');
paragraphs.forEach(paragraph => {
const para = document.createElement("p");
const node = document.createTextNode(paragraph);
para.appendChild(node);
results.appendChild(para);
});
<div id="results">
</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);
}
}
});
});
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/
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();
}
}