Uncaught RangeError: Invalid string length in JavaScript - javascript

I have a JavaScript function:
function addcontent(ele, text, lines, separate_lines){
if(separate_lines){
for(i=0; i<lines; i++){
text=text+"\n"+text+"\n";
}
}
else if(!separate_lines){
for(e=0; e<lines; e++){
text=text+text;
}
}
ele.append(text);
}
and when I use it for the onclick of an HTML Button like:
<button onclick="addcontent(document.body, 'A', 100, true)">
Add content.
</button>
I get this error in the Chrome console when I click the button:
Uncaught RangeError: Invalid string length
at addcontent
at HTMLButtonElement.onclick
I have no idea where I'm wrong.

The issue is that on each iteration you add the text two times, but you assign the result to text again.
iteration 1: A + A = AA (but you assign to text) so
iteration 2: AA + AA = AAAA
iteration 3: AAAA + AAAA = AAAAAAAA
iteration 4: AAAAAAAA + AAAAAAAA = AAAAAAAAAAAAAAAA
etc
So it is exponential. You are basically doing 2lines insertion of the text. You can guess that this creates a "quite" large string and it cannot be handled.
Use a different variable for the generated text.
function addcontent(ele, text, lines, separate_lines) {
let finalText = text;
if (separate_lines) {
for (let i = 0; i < lines; i++) {
finalText = finalText + "\n" + text;
}
} else {
for (let e = 0; e < lines; e++) {
finalText = finalText + text;
}
}
ele.append(finalText);
}
<button onclick="addcontent(document.body, 'A', 100, true)">
Add content.
</button>

It's the old Wheat and chessboard problem, the string is simply way too long.
You double the string for 100 times that's 2^100...
It works fine with say lines = 10

This problem can be resolved if you can do as:
text = (text + "\n").repeat(lines * 2);
function addcontent(ele, text, lines, separate_lines) {
if (separate_lines) {
text = (text + "\n").repeat(lines * 2);
} else if (!separate_lines) {
for (e = 0; e < lines; e++) {
text = text + text;
}
}
ele.append(text);
}
<button onclick="addcontent(document.body, 'A', 100, true)">
Add content.
</button>
If you want to add new line in after A
function addcontent(ele, text, lines, separate_lines) {
if (separate_lines) {
text = `${text}<br>`.repeat(lines * 2);
} else if (!separate_lines) {
for (e = 0; e < lines; e++) {
text = text + text;
}
}
ele.innerHTML = text;
}
<button onclick="addcontent(document.body, 'A', 100, true)">
Add content.
</button>

Related

Pure Javascript - get beginning position of current line in a multiline textarea

I'm looking for a pure javascript answer as that reflects my projects scope. jQuery answers will not be marked correct, but are welcomed for future question seekers. Side note: I am also not interested in third party libraries.
I'm trying to get the first position (0) of the current line (current is based of selectionStart the chance the user selects more than 1 line) in a multiline textarea, but translate that to caret index.
What have I tried? Nothing pretty:
for ( i = 0; i < this.selectionStart; i++ ) {
if (this.value.substr(i,1).match(/\r?\n|\r/)) {
lineStartIndx = i + 1
}
}
This is proving to be costly when iterating textareas with huge amounts of lines. My personal usage of this will not be executed every keydown, however, I just used it as an example. Are there any better methods, built in or otherwise to emulate this outcome?
My full example:
var input = document.getElementById("ta");
var output = document.getElementById("output");
let pattern = /\r?\n|\r/;
var lineNum, lineStartIndx;
input.addEventListener("keydown", function(e) {
taHandler(this);
})
input.addEventListener("click", function(e) {
taHandler(this);
})
function taHandler(elem) {
lineNum = getLineNumForSelection(elem);
let caretPos = elem.selectionStart;
lineStartIndx = 0;
for ( i = 0; i < caretPos; i++ ) {
if (elem.value.substr(i,1).match(pattern)) {
lineStartIndx = i + 1
}
}
output.innerHTML = "Selection Start: " + caretPos + " Selection End: " + elem.selectionEnd +
" <br> Line Number: " + lineNum.start +
"<br>Line Start Position: " + lineStartIndx;
}
function getLineNumForSelection(sel) {
return {
'start' : sel.value.substr(0, sel.selectionStart).split(pattern).length,
'end' : sel.value.substr(0,sel.selectionEnd).split(pattern).length
};
}
<textarea id="ta" rows="5" cols="50">
Line one
Line two
Line three
Line four
</textarea>
<hr>
<div id="output"></div>
The method in my copy of the snippet splits the content into lines and uses the .length property instead of a loop so it looks "prettier" but according to time complexity of javascript's .length may not be any faster since there is nothing in the spec that prevents slow browser implementation.
Two side notes about the code, I'd use lineStartIndex, not lineStartIndx without the e. Since lineNum is an array, I'd use lineNumArray or selLineNums or something that is more obvious since variables ending in num are generally integers.
var input = document.getElementById("ta");
var output = document.getElementById("output");
let pattern = /\r?\n|\r/;
var lineNum, lineStartIndx;
input.addEventListener("keydown", function(e) {
taHandler(this);
})
input.addEventListener("click", function(e) {
taHandler(this);
})
function taHandler(elem) {
lineNum = getLineNumForSelection(elem);
let caretPos = elem.selectionStart;
lineStartIndx = 0;
// begin modified code
let lines = elem.value.split(pattern),
lineIndex = 0;
while ( (lineIndex + 1 ) < lineNum.start ) {
lineStartIndx += parseInt( lines[lineIndex].length ) + 1;
lineIndex++;
}
// end modified code
// begin replaced code
for ( i = 0; i < caretPos; i++ ) {
if (elem.value.substr(i,1).match(pattern)) {
lineStartIndx = i + 1
}
}
// end replaced code
output.innerHTML = "Selection Start: " + caretPos + " Selection End: " + elem.selectionEnd +
" <br> Line Number: " + lineNum.start +
"<br>Line Start Position: " + lineStartIndx;
}
function getLineNumForSelection(sel) {
return {
'start' : sel.value.substr(0, sel.selectionStart).split(pattern).length,
'end' : sel.value.substr(0,sel.selectionEnd).split(pattern).length
};
}
<textarea id="ta" rows="5" cols="50">
Line one
Line two
Line three
Line four
</textarea>
<hr>
<div id="output"></div>

Truncate text but keep html in javascript

I am printing a string some text, some other text.
I want to truncate the visible text, but keeping the link.
If I just did a naïve substring, I would mess up the html.
I want to make sure that only 100 characters are shown, but if the last part of the string is, for instance, <a hre, then this should be stripped as well.
Edit
I have tried
arr = ['some text', 'some other text', 'some third text'];
output = arr.map(el => '' + el + '').join(', ');
// print
console.log(output.substr(0, 20))
but this will cut off the html and output
<a href="#">some tex
But I want it to count the number of shown characters instead of how many characters were used to show the output.
So if the shown output is some text, some other text, some third text, I want it to cut it off at character 20 in the output text rather than character 20 in the html output.
Set textContent of elements to anything you want:
(function() {
var links = document.getElementsByClassName("link");
for(var i = 0; i < links.length; i++) {
links[i].textContent = "Link text changed";
}
})();
<a class="link" href="http://stackexchange.com">Stack Exchange</a>
<a class="link" href="http://stackoverflow.com">Stack Overflow</a>
You can do this with simple CSS.
var paragraph = document.querySelector('p#before');
document.querySelector('p#after-text').innerHTML = truncateTEXT(paragraph, 20, true);
document.querySelector('p#after-html').innerHTML = truncateHTML(paragraph, 20, true);
function truncateHTML(html, limit, dots) {
holdCounter = false;
truncatedHTML = '';
html = paragraph.innerHTML;
for(index = 0; index < html.length; index++) {
if(!limit) {
break;
}
if(html[index] == '<') {
holdCounter = true;
}
if(!holdCounter) {
limit--;
}
if(html[index] == '>') {
holdCounter = false;
}
truncatedHTML += html[index];
}
truncatedHTML = correctHTML(truncatedHTML);
if(dots) {
truncatedHTML = truncatedHTML + '...';
}
return truncatedHTML;
}
function truncateTEXT(string, limit, dots) {
string = string.innerText;
if(string.length > limit) {
return string.substring(0, limit) + (dots ? '...' : '');
} else {
return string;
}
}
function correctHTML(html){
container = document.createElement('div');
container.innerHTML = html
return container.innerHTML;
}
<p id="before">Lorem <strong>ipsum</strong> dolor <strong>sit</strong> amet <strong>consectetur</strong> adipiscing <strong>elit</strong></p>
<p id="after-text"></p>
<p id="after-html"></p>

Create form for function Html

This is my problem, hope get some support for this.
This is my function.
function show(n,q)
{
for(j=1;j<=n;j++)
{
s=j.toString().length;
t=0;
for(i=s-1;i>=0;i--)
{
t+=Math.pow((Math.floor(j/Math.pow(10,i))%10),q);
}
if(t==j){document.write(t+ " ");}
else{document.write("");}
}
}
show(1000,3);
With two inputs: number n and the exponent q it will solve all the numbers smaller than n which the sum of all the q-exponented of its digits is equal to itself.
For example: q=3, n=200, we have the number 153 because: 1^3 + 5^3 + 3^3 = 153
This function is OK, but due to my bad javascript skill, I dont know how to create a form alowing to enter n and q into 2 boxes, then click button "Show" we have results in the third box.
I have tried this below code, but it does not work :(
<input id='number' type='text' />
<input id='exp' type='text' />
<button onclick='javascript:show()'>Show</button>
<div id='res' style='width:100%;height:200px'></div>
<script>
function show() {
var n=document.getElementById('number').value,
var q=document.getElementById('exp').value,
out=document.getElementById('res'),
out.innerHTML="";
for(j=1;j<=n;j++)
{
s=j.toString().length;
t=0;
for(i=s-1;i>=0;i--)
{
t+=Math.pow((Math.floor(j/Math.pow(10,i))%10),q);
}
if(t==j){
out.innerHTML+=t+ " ";
}
else{
out.innerHTML+="";
}
}
}
</script>
In additon, I want to do it myself, could you guys tell me where i can find guide for this problem.
Thanks.
Your code has some punctuation issues.
Try to replace:
var n=document.getElementById('number').value,
var q=document.getElementById('exp').value,
out=document.getElementById('res'),
out.innerHTML="";
by
var n=document.getElementById('number').value,
q=document.getElementById('exp').value,
out=document.getElementById('res');
out.innerHTML="";
The code looks fine and will do what you are trying to do. Just there are some , (Comma) instead of ; (Semi-colon) in your code. Change them and then try.
Check the solution here.
http://jsfiddle.net/JszG2/
var n=document.getElementById('number').value;
var q=document.getElementById('exp').value;
out=document.getElementById('res');
Below is solution using JQuery....
<script>
function show() {
var num = parseInt($('#number').val());
var exp = parseInt($('#exp').val());
out = $('#res');
var num = document.getElementById('number').value;
var exp = document.getElementById('exp').value;
out = document.getElementById('res');
out.innerHTML = "";
for (p = 1; p <= num; p++) {
q = p.toString().length;
v = 0;
for (i = q - 1; i >= 0; i--) {
v = v+ Math.pow((Math.floor(p / Math.pow(10, i)) % 10), exp);
}
if (v == p) {
out.innerHTML += v + " ";
}
else {
out.innerHTML += "";
}
}
}
</script>

Textarea split text into rows and cols

Hi I am working over some textarea which needs to have this kind of functionality: I get in config max lines number and max characters per line number. But I can't think of any algorithm for splitting text into this config values. It would be easy but I have to take into considaration that user can break text by himself and this should be included... Can anyone help me with that?
Please also note that I am not using monospaced font.
I have wrote some code which presents what I am trying to achieve:
splitIntoLines:function (str, lines, maxCharactersPerLine) {
var strLen = str.length,
counter = maxCharactersPerLine,
newStr = '';
if (str.length > 0) {
for (var i = 0; i < strLen; i++) {
newStr += str[i];
counter -= 1;
if (str[i] === '\n' || str[i] === '\r\n' || counter < 0) {
counter = maxCharactersPerLine;
}
if (counter === 0 && this.countLines(newStr) < lines) {
newStr += this.newLine;
}
}
}
if(newStr.length > this.maxChars){
newStr = newStr.substring(0, this.maxChars)
}
return newStr;
}
This function is called every keyUp event. But I think that it isn't best way and it has some bugs.
Simple.. while giving textarea define rows and cols properties to that textarea like
<textarea rows="10" cols="30"></textarea>
try this
<script type="text/javascript">
$(document).ready(function () {
var max_line = 4, max_char = 10;
$('#addAccordion').click(function (event) {
var lines = $('#cmt_content').val().split('\n');
for (var i = 0; i < lines.length; i++) {
if (lines[i].length > max_char) {
alert('character length is more than specified');
}
}
if (lines.length > max_line) {
alert('new line is not allowed');
}
})
});
</script>
<div>
<textarea id="cmt_content" rows="10" cols="60"></textarea>
<br>
<input id="addAccordion" type="button" value="ADD COMMENT" />
</div>

struggling with creating asterisks in Javascript

I've been struggling with this for some time now. What I wanted to create is to output a triangle of asterisks based on user's input. Let say user entered size 5, it would look something like this:
*
**
***
****
*****
My HTML looks like:
<p>
Size: <input type="text" id="size">
<input type="button" value="Draw" onclick="draw()">
</p>
<pre id="output">
</pre>
In my Javascript, I have:
function draw()
{
var size = customJS.get ( "size" ); //I have a custom library where it get the Id from HTML
var theTriangle = makeTriangle( size.value ); //sending in the size
customJS.set ("output", theTriangle); //will set theTriangle to display to "output" in HTML
}
function makeTriangle( theSize )
{
var allLines = ""; // an empty string to hold the entire triangle
for ( var i = 0; i <= size; i++) // this loop size times
{
var oneLine = createLine ( i <= size ); // amount of asterisks for this line
allLines += oneLine;
}
return allLines;
}
function createLine ( length )
{
var aLine = ""; // an empty string to hold the contents of this one line
for ( var j = 0; j <= i; j++ ) //this loop length times
{
aLine += '*';
}
return aLine + "<br>";
}
anyone have any tip on how I go about this? thank you so much!
Newlines in HTML normally display as spaces, but you want them to show as newlines. The pre tag makes newlines actually appear as new lines, so wrap the output in a pre tag:
customJS.set ("output", "<pre>" + theTriangle + "</pre>");
Also, you're calling createLine like this:
var oneLine = createLine ( i <= size );
i <= size yields a boolean (true or false) rather than a number. You probably mean to just pass it i:
var oneLine = createLine ( i );
Additionally, you're setting size like this:
var size = customJS.get = ( "size" );
You probably want to drop the second equals, since as is, it sets the variable size to the string "size".
And finally, you've got a few variables wrong: in makeTriangle, you're looping size times, but size is undefined; you probably meant theSize. In createLine, you're looping i times, but i is undefined; you probably meant length.
With all that, it works.
There were several bugs in your code. For example using theSize instead size as parameter in the function makeTriangle(), using i instead of length in the createLine() function in the for loop condition.
Another one was:
use
return aLine + "<br/>";
instead of
return aLine + "\n";
The working solution for your code can be found in this jsFiddle: http://jsfiddle.net/uwe_guenther/wavDH/
And below is a copy of the fiddle:
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>Size:
<input type="text" id="sizeTextField">
<input id='drawButton' type="button" value="Draw">
<div id='output'></div>
</p>
<script src='main.js'></script>
</body>
</html>
main.js
(function (document) {
var drawButton = document.getElementById('drawButton'),
sizeTextField = document.getElementById('sizeTextField'),
output = document.getElementById('output');
function makeTriangle(size) {
var allLines = '';
for (var i = 0; i <= size; i++) {
var oneLine = createLine(i); // amount of asterisks for this line
allLines += oneLine;
}
return allLines;
}
function createLine(length) {
var aLine = '';
for (var j = 0; j <= length; j++) {
aLine += '*';
}
return aLine + "<br/>";
}
drawButton.onclick = function () {
output.innerHTML = makeTriangle(sizeTextField.value);
};
})(document);
You can leverage some JavaScript tricks to make the code a bit more terse:
<div style="text-align: center">
<label>Size:
<input type="text" id="size" value="5">
</label> <pre id='output'></pre>
</div>
<script>
var size = document.getElementById('size'),
output = document.getElementById('output');
function update() {
var width = +size.value, // Coerce to integer.
upsideDown = width < 0, // Check if negative.
width = Math.abs(width), // Ensure positive.
treeArray = Array(width).join('0').split('0') // Create an array of 0s "width" long.
.map(function(zero, level) { // Visit each one, giving us the chance to change it.
return Array(2 + level).join('*'); // Create a string of *s.
});
upsideDown && treeArray.reverse(); // If width was negative, stand the tree on its head.
output.innerHTML = treeArray.join('\n'); // Join it all together, and output it!
}
size.onkeyup = update;
update();
size.focus();
</script>
http://jsfiddle.net/mhtKY/4/

Categories

Resources