The following fiddle allows text to be pasted into a <textarea>
and generated into equal paragraphs dynamically <p>
consisting of the same amount of characters.
The problem occurring is; text from previous dynamically generated paragraphs <p>
overflows within each tag and does not continue to the next dynamic paragraph properly. Therefore, is it possible for the user to press enter and move that content down into the next existing paragraph, while still keeping the existing formatting dynamically and automatically?
If a new Fiddle could be provided, it would be very much appreciated, as I am still new to coding. Once again, the Fiddle can be found here.
Update: Is it possible for once the paragraphs are generated, for the user to press enter and if possible move their content down into the below paragraph seamlessly? And also for the same to apply when the backspace button is pressed, for the content to move up into the above paragraph? The problem occurring is, the text, when pressing enter, seems to hide the text due to the overflow property in css.
$(function() { $("#Go").on('click', function() { var theText = $('textarea').val(); var numberOfCharacters = 300; while (theText.length) { while (theText.length > numberOfCharacters && theText.charAt(numberOfCharacters) !== ' ') { numberOfCharacters++; } $("#text_land").append("<br><\/br><p>" + theText.substring( 0, numberOfCharacters) + "<\/p><br><\/br>"); theText = theText.substring(numberOfCharacters); numberOfCharacters = 300; $('p').attr('contenteditable', 'true'); $("p").addClass("text"); } }) }) $('select').on('change', function() { var targets = $('#text_land p'), property = this.dataset.property; targets.css(property, this.value); }).prop('selectedIndex', 0); (end);
@media print { p { page-break-inside: avoid; } } p { position: relative; } @media print { .no-print,.no-print * { display: none !important; } } p { border-style: solid; color: #000; display: block; text-align: justify; border-width: 5px; font-size: 19px; overflow: hidden; height: 300px; width: 460px; word-wrap: break-word; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div align="center"> <h4 align="center"><u>Paste text in the field below to divide text into paragraphs.</u></h4><br> <br> <textarea placeholder="Type text here, then press the button below." cols="50" id="textarea1" rows="10"> </textarea><br> <br> <button id="Go">Divide Text into Paragraphs!</button> </div> <hr> <h2 align="center">Divided Text Will Appear Below:</h2> <div> <div align="center" id="text_land" style="font-family: monospace"> </div> </div>
The HTML <p> element defines a paragraph. A paragraph always starts on a new line, and browsers automatically add some white space (a margin) before and after a paragraph.
You can separate your paragraphs with first-line indentation instead of margins by using the CSS text-indent property. If you need more blank space between paragraphs, use the CSS margin property. Don't add empty paragraphs, as it may confuse the users of assistive technologies.
This JavaScript function is defined in the <script> tag in the <head> element. The addText() function first assigns text typed in the text field to the sentence variable. Next, the script creates a new text node containing the value of the sentence variable and then appends the new text node to the paragraph.
Very simple, if I understand you correctly.
$(function() { $("#Go").on('click', function() { var theText = $('textarea').val(); var paragraphs = theText.split('\n\n'); $("#text_land").html(''); paragraphs.forEach(function(paragraph) { var lines = paragraph.split('\n'); $('<p class="text" contenteditable />').html(lines.join('<br>')).appendTo("#text_land"); }); }) }) $('select').on('change', function() { var targets = $('#text_land p'), property = this.dataset.property; targets.css(property, this.value); }).prop('selectedIndex', 0); (end);
@media print { p { page-break-inside: avoid; } } p { position: relative; } @media print { .no-print,.no-print * { display: none !important; } } p { border-style: solid; color: #000; display: block; text-align: justify; border-width: 5px; font-size: 19px; overflow: hidden; height: 300px; width: 460px; word-wrap: break-word; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div align="center"> <h4 align="center"><u>Paste text in the field below to divide text into paragraphs.</u></h4><br> <br> <textarea placeholder="Type text here, then press the button below." cols="50" id="textarea1" rows="10"> </textarea><br> <br> <button id="Go">Divide Text into Paragraphs!</button> </div> <hr> <h2 align="center">Divided Text Will Appear Below:</h2> <div> <div align="center" id="text_land" style="font-family: monospace"> </div> </div>
possible for the user to press enter and move that content down into the next existing paragraph, while still keeping the existing formatting dynamically and automatically
If I understand you correctly, what you want is that once the text is divided into paragraphs, and then the user adds some text into one of them and presses Enter, then the remaining text should flow into the next paragraphs distributing the overflowing text equally as done earlier.
Similarly, when the user presses BackSpace at the start of the paragraph, then the text goes back into the previous paragraph again with the overflowing text distributed into the other paragraphs equally as done earlier.
As an algorithm, what you need is something like this:
p
s dynamically as required.keyup
event on those p
elementsWith that rough algorithm, you can start coding which could look something like this:
Note 1: This is all JavaScript, no jQuery.
Note 2: This is overly simplified, you will need to further optimize and work out all the edge cases.
Cache required elements and bind event handlers:
var btn = document.getElementById('go'), textarea = document.getElementById('textarea1'), content = document.getElementById('content'); btn.addEventListener('click', initialDistribute); content.addEventListener('keyup', handleKey);
Distribute the initial text from the textarea
removing existing paragraphs if any:
function initialDistribute() { var text = textarea.value; while (content.hasChildNodes()) { content.removeChild(content.lastChild); } rearrange(text); }
Logic for re-aranging / distributing text by creating required number of paragraphs dynamically:
function rearrange(text) { var chunks = text.match(/.{1,100}/g) || []; chunks.forEach(function(str, idx) { para = document.createElement('P'); para.setAttribute('contenteditable', true); para.textContent = str; content.appendChild(para); }); }
Note 3: I've used 100 characters to split text for this example. Also, this does not take care of spaces and will split the words in between. You will need to do that in your code. (# see edit below)
Event handler for trapping Enter (keycode 13) and BackSpace (keycode 8) keys. Also, see if the element is a p
element :
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) { return; } ...
Get the cursor position to determine whether BackSpace was pressed at the beginning of the paragraph or not:
position = window.getSelection().getRangeAt(0).startOffset;
If Enter was pressed, extract text after the last child of the current paragraph (contenteditable will produce a div
when Enteris pressed), remove that node, prepend the remaining text of all paragraphs which come after this, and remove the remaining paragraphs.
if (key == 13) { fragment = para.lastChild; overflow = fragment.textContent; fragment.parentNode.removeChild(fragment); remainingText = overflow + removeSiblings(para, false); rearrange(remainingText); }
If BackSpace was pressed, check if there is a preceding paragraph and that the cursor was at the beginning. If yes, extract remaining text from all succeeding paragraphs (including the current one) while removing them:
if (key == 8 && para.previousElementSibling && position == 0) { fragment = para.previousElementSibling; remainingText = removeSiblings(fragment, true); rearrange(remainingText); }
Logic to extract text from succeeding paragraphs and removing those:
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; }
Putting that all together, here is a working snippet:
Snippet:
var btn = document.getElementById('go'), textarea = document.getElementById('textarea1'), content = document.getElementById('content'), chunkSize = 100; btn.addEventListener('click', initialDistribute); content.addEventListener('keyup', handleKey); 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) { 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 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; }
* { 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: 0.5em; } p { padding: 1.2em 0.5em; margin: 1.4em 0; border: 1px dashed #aaa; }
<div> <h3>Paste text in the field below to divide text into paragraphs..</h3> <textarea placeholder="Type text here, then press the button below." id="textarea1" rows="5" ></textarea><br/><br/> <button id="go">Divide Text into Paragraphs</button> </div> <hr> <div> <h3>Divided Text Will Appear Below:</h3> <div id="content"></div> </div>
And a Fiddle for you to play with:
Fiddle: https://jsfiddle.net/abhitalks/jwnnn5oy/
Fixed the regex for breaking at word boundaries. Also added the non-regex procedural code for the same (on the lines of Op's original code), to demonstrate the Op on how to factor-in and integrate his other code segments into it.
Note 4: Regarding Op's comment of using jQuery, it has got nothing to do with the problem at hand. jQuery is nothing but JavaScript and should be trivial for them to incorporate pieces of snippet into the larger code base.
Change Set: Added function splitText
.
As per your comment, if you want the re-distribution to happen automatically as the user types... then you will need to calculate the length of the text in that paragraph and see if exceeds your chunk-size. If it does, then do the re-distribution from that paragraph onwards. Do the reverse for backspace.
I initially posted the solution for your requirement of doing that when user presses enter between any text to break and distribute that to succeeding paragraphs. I do not recommend doing that automatically as user types because the changes will be too jarring for the user.
Snippet 2:
var btn = document.getElementById('go'), textarea = document.getElementById('textarea1'), content = document.getElementById('content'), chunkSize = 100; btn.addEventListener('click', initialDistribute); content.addEventListener('keyup', handleKey); 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 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; }
* { 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: 0.5em; } p { padding: 1.2em 0.5em; margin: 1.4em 0; border: 1px dashed #aaa; }
<div> <h3>Paste text in the field below to divide text into paragraphs..</h3> <textarea placeholder="Type text here, then press the button below." id="textarea1" rows="5" ></textarea><br/><br/> <button id="go">Divide Text into Paragraphs</button> </div> <hr> <div> <h3>Divided Text Will Appear Below:</h3> <div id="content"></div> </div>
Fiddle 2: https://jsfiddle.net/abhitalks/hvhr4ds8/
Note 5: In the fiddle first hit enter to break some text in between so that you are able to see how re-distribution happens as you type. Also, note that because of the logic of not breaking a word, it will take a few more characters before it re-distributes.
Change Set: Added function redistributeAuto
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With