Javascript 'Dot Dot Dot' While Calculating - javascript

I have a function to display dot dot dot in a popup window while generating a report. I want to display the text:
Generating Report.
Generating Report..
Generating Report...
...repeat, until the report is ready. So far I've only been able to get the three dots in the popup without the other text. Here is my code snippet:
on(link, "click", function(){
var dots = window.setInterval( function() {
var wait = document.getElementById("reportLink");
if ( wait.innerHTML.length > 2)
wait.innerHTML = "";
else
wait.innerHTML += ".";
}, 400);
domAttr.set(dom.byId("reportLink"), "innerHTML", dots);
I tried this but it didn't work:
domAttr.set(dom.byId("reportLink"), "innerHTML", "Generating Report" + dots);

The function you're passing to setInterval is directly manipulating the dom element instead of returning a value (which wouldn't work anyway), so you "Generating Report" + dots won't work since dots is just a reference to the setInterval function. Instead, you need to modify your setInterval function using something like this (untested since I'm not sure what JS library/framework you're using):
var dots = window.setInterval( function() {
var wait = document.getElementById("reportLink"),
msg = "Generating Report",
msgLen = msg.length;
// Initialize to msg if blank
wait.innerHTML = (wait.innerHTML === "")
? msg
: wait.innerHTML;
if (wait.innerHTML.length >= (msgLen + 2))
wait.innerHTML = msg;
else
wait.innerHTML += ".";
}, 400);
Instead of initializing and testing your message by empty string, this uses the actual message you want to show. I also made the number of dots configurable by variable instead of using a "magic number".
[EDIT 1]
Make sure you remove this line (and any derivatives):
domAttr.set(dom.byId("reportLink"), "innerHTML", dots);
The function you're passing to setInterval is doing all the work.
[EDIT 2: Fixed the excessive dots on first run by initializing to msg if the reportLink element is empty.]

Try this:
var dots = window.setInterval( function() {
var wait = document.getElementById("reportLink");
if ( wait.innerHTML.length > 19)
wait.innerHTML = "";
else
wait.innerHTML += ".";
}, 400);
var text = "Generating Report" + dots;
domAttr.set(dom.byId("reportLink"), "innerHTML", text);
When you check the length you need to include the length of 'Generating report'.

maybe this?
var wait = document.getElementById("reportLink");
var loop = 0;
var text = "Generating Report";
window.setInterval( function() {
if ( loop > 2) {
wait.innerHTML = text;
loop = 0
} else {
wait.innerHTML += ".";
loop++
}
}, 400);

Related

Checking a div for duplicates before appending to the list using jQuery

This should be trivial but I'm having issues...
Basically what I am trying to do is append a new "div" to "selected-courses" when a user clicks on a "course". This should happen if and only if the current course is not already in the "selected-courses" box.
The problem I'm running into is that nothing is appended to the "selected-courses" section when this is executed. I have used alert statements to make sure the code is in fact being run. Is there something wrong with my understanding of the way .on and .each work ? can I use them this way.
Here is a fiddle http://jsfiddle.net/jq9dth4j/
$(document).on("click", "div.course", function() {
var title = $( this ).find("span").text();
var match_found = 0;
//if length 0 nothing in list, no need to check for a match
if ($(".selected-course").length > 0) {
match_found = match(title);
}
if (matched == 0) {
var out = '<div class="selected-course">' + '' + title + ''+'</div>';
$("#selected-box").append(out);
}
});
//checks to see if clicked course is already in list before adding.
function match(str) {
$(".selected-course").each(function() {
var retval = 0;
if(str == this.text()) {
//course already in selected-course section
retval = 1;
return false;
}
});
return retval;
}
There was a couple of little issues in your fiddle.
See fixed fiddle: http://jsfiddle.net/jq9dth4j/1/
function match(str) {
var retval = 0;
$(".selected-course").each(function() {
if(str == $(this).text()) {
retval = 1;
return false;
}
});
return retval;
}
You hadn't wrapped your this in a jquery object. So it threw an exception saying this had no method text().
Second your retval was declared inside the each so it wasn't available to return outside the each, wrong scope.
Lastly the if in the block:
if (matched== 0) {
var out = '';
out += '<div class="selected-course">' + '' + title + ''+'</div>';
$("#selected-box").append(out);
}
was looking at the wrong variable it was looking at matched which didn't exist causing an exception.
Relying on checking what text elements contain is not the best approach to solve this kind of question. It is prone to errors (as you have found out), it can be slow, it gives you long code and it is sensitive to small changes in the HTML. I would recommend using custom data-* attributes instead.
So you would get HTML like this:
<div class="course" data-course="Kite Flying 101">
<a href="#">
<span>Kite Flying 101</span>
</a>
</div>
Then the JS would be simple like this:
$(document).on('click', 'div.course', function() {
// Get the name of the course that was clicked from the attribute.
var title = $(this).attr('data-course');
// Create a selector that selects everything with class selected-course and the right data-course attribute.
var selector = '.selected-course[data-course="' + title + '"]';
if($(selector).length == 0) {
// If the selector didn't return anything, append the div.
// Do note that we need to add the data-course attribute here.
var out = '<div class="selected-course" data-course="' + title + '">' + title + '</div>';
$('#selected-box').append(out);
}
});
Beware of case sensitivity in course names, though!
Here is a working fiddle.
Try this code, read comment for where the changes are :
$(document).on("click", "div.course", function () {
var title = $(this).find("span").text().trim(); // use trim to remove first and end whitespace
var match_found = 0;
if ($(".selected-course").length > 0) {
match_found = match(title);
}
if (match_found == 0) { // should change into match_found
var out = '';
out += '<div class="selected-course">' + '' + title + '' + '</div>';
$("#selected-box").append(out);
}
});
function match(str) {
var retval = 0; // this variable should place in here
$(".selected-course").each(function () {
if (str == $(this).find('a').text().trim()) { // find a tag to catch values, and use $(this) instead of this
retval = 1;
return false;
}
});
return retval; // now can return variable, before will return undefined
}
Updated DEMO
Your Issues are :
1.this.text() is not valid. you have to use $(this).text().
2.you defined var retval = 0; inside each statement and trying to return it outside each statement. so move this line out of the each statement.
3.matched is not defined . it should be match_found in line if (matched == 0) {.
4. use trim() to get and set text, because text may contain leading and trailing spaces.
Your updated JS is
$(document).on("click", "div.course", function () {
var title = $(this).find("span").text();
var match_found = 0;
if ($(".selected-course").length > 0) {
match_found = match(title);
}
if (match_found == 0) {
var out = '<div class="selected-course">' + '' + title + '' + '</div>';
$("#selected-box").append(out);
}
});
function match(str) {
var retval = 0;
$(".selected-course").each(function () {
if (str.trim() == $(this).text().trim()) {
retval = 1;
return false;
}
});
return retval;
}
Updated you Fiddle

How to force loop to wait until user press submit button?

I have simple function which checks if entered pin code is valid. But i don't know how to force for-loop to wait until i enter code again to check again it's validity.
So how it should be - i type PIN code, then click OK button and it checks whether it's correct (if it is, i can see my account menu; if it's not i have to type it again and i have 2 chances left). My code fails, because PIN when code is wrong program should wait until i type new code and press OK button again.
I tried setTimeout(), callback(), but it doesn't work. This is what i have - a function with for-loop that just runs 3 times (as it is suppose to, but not instantly) without giving a chance to correct the PIN code.
That's whole, unfinished yet, code: http://jsfiddle.net/j1yz0zuj/
Only function with for-loop, which checks validity of PIN code:
var submitKey = function(callback)
{
console.log("digit status" + digitStatus);
if (digitStatus == 0)
{
correctPIN = 1234;
var onScreen = document.getElementById("screen");
for (i=0; i<3; i++)
{
if (onScreen.innerHTML.slice(15, onScreen.innerHTML.length) == correctPIN)
{
setTimeout(accountMenu, 1250);
//break;
}
else
{
onScreen.innerHTML += "<br> Błędny kod PIN! Wpisz PIN ponownie. <br> Pozostało prób: " + (2-i);
callback();
//cardInserted = function(function(){console.log("Ponowne wpisanie PINu");});
}
if (i=2) console.log("blokada");
}
}
else if (digitStatus == 1)
{
}
}
Your approach is wrong. You should not make the user wait!!! You need 2 more variables at the top of your programm pincount=0 and pininputallowed. Increase pincount in the submit key function by 1 and then check if pincount<3.
Here is a corrected version of your code.
http://jsfiddle.net/kvsx0kkx/16/
var pinCount=0,
pinAllowed=true;
var submitKey = function()
{
console.log("digit status" + digitStatus);
if (digitStatus == 0)
{
correctPIN = 1234;
var onScreen = document.getElementById("screen");
pinCount++;
if(pinCount >= 3) {
pinAllowed = false;
onScreen.innerHTML = "<br>blokada";
}
if(pinAllowed){
if (onScreen.innerHTML.slice(15, onScreen.innerHTML.length) == correctPIN)
{
setTimeout(accountMenu, 1250);
//break;
}
else
{
onScreen.innerHTML += "<br> Błędny kod PIN! Wpisz PIN ponownie. <br> Pozostało prób: " + (3-pinCount);
inputLength = 0;
document.getElementById("screen").innerHTML += "<br>Wpisz kod PIN: ";
//callback();
//cardInserted = function(function(){console.log("Ponowne wpisanie PINu");});
}
}
}
else if (digitStatus == 1)
{
}
}
You need to create much more variables to control your machine. Your add/delete digit function had conditions that were badly written and only worked if the text on the screen was short enough.
var inputLength = 0;
addDigit = function(digit){
//numKeyValue = numKeyValue instanceof MouseEvent ? this.value : numKeyValue;{
if (inputLength < pinLength) {
onScreen.innerHTML += this.value;
inputLength++;
}
//if (onScreen.innerHTML == 1234) console.log("PIN został wprowadzony");
},
delDigit = function(){
if (inputLength >= 0) {
onScreen.innerHTML = onScreen.innerHTML.slice(0, -1);
inputLength--;
}
};
If you want to empty the screen at any moment you can insert onScreen.innerHTML = ''; anywhere
ps: Thanks for the exercise and nice automat you made there.

Refresh function per 3 seconds

function random_imglink() {
var myimages = new Array()
//specify random images below. You can have as many as you wish
myimages[1] = "This is text1."
myimages[2] = "This is text2."
myimages[3] = "This is text3."
myimages[4] = "This is text4."
myimages[5] = "This is text5."
myimages[6] = "This is text6."
var ry = Math.floor(Math.random() * myimages.length)
if (ry == 0)
ry = 1
document.write('<h1 class="comingsoon">' + myimages[ry] + '</h1>')
}
random_imglink()
How do you make it so that the above code gets refreshed every 3 seconds? Like, the output is refreshed every 3 seconds. Thanks.
setInterval is easier way for your query.
Simple Example:
setInterval(function(){ alert("Hello"); }, 3000);
Don't use document.write. Use document.innerHTML instead.
The write() method is mostly used for testing: If it is used after an HTML document is fully loaded, it will delete all existing HTML.
And of course the setInterval, as the other answers suggest.
I've created a fiddle that I think does what you want.
I hope you like cats.
http://jsfiddle.net/alexjamesbrown/9cu5L666/
Note - I'm setting the inner html of a div, rather than the whole document
function random_imglink() {
var myimages = new Array()
//specify random images below. You can have as many as you wish
myimages[1] = "http://placekitten.com/g/100/400"
myimages[2] = "http://placekitten.com/g/200/250"
myimages[3] = "http://placekitten.com/g/300/234"
myimages[4] = "http://placekitten.com/g/500/300"
myimages[5] = "http://placekitten.com/g/100/400"
myimages[6] = "http://placekitten.com/g/430/100"
var ry = Math.floor(Math.random() * myimages.length)
if (ry == 0)
ry = 1;
var div = document.getElementById('myContainer');
div.innerHTML='<h1 class="comingsoon"><img src="' + myimages[ry] + '"</img></h1>'
}
setInterval(function(){ random_imglink(); }, 3000);
You can use setInterval
window.setInterval(random_imglink,3000);
You can use
window.setInterval(random_imglink, 3000);
but be cautious because if your browser does not render the content in the time interval you should expect choppiness and dropped frames. A much better approach is to use requestAnimationFrame
The Window.requestAnimationFrame() method tells the browser that you
wish to perform an animation and requests that the browser call a
specified function to update an animation before the next repaint. The
method takes as an argument a callback to be invoked before the
repaint.
function random_imglink() {
var myimages = new Array()
//specify random images below. You can have as many as you wish
myimages[1] = "This is text1."
myimages[2] = "This is text2."
myimages[3] = "This is text3."
myimages[4] = "This is text4."
myimages[5] = "This is text5."
myimages[6] = "This is text6."
var ry = Math.floor(Math.random() * myimages.length)
if (ry == 0)
ry = 1
document.body.innerHTML = '<h1 class="comingsoon">' + myimages[ry] + '</h1>';
}
window.setInterval(random_imglink, 3000);

jQuery "keyup" crashing page when checking 'Word Count'

I have a word counter running on a DIV and after typing in a few words, the page crashes. The browser continues to work (par scrolling) and no errors are showing in Chrome's console. Not sure where I'm going wrong...
It all started when I passed "wordCount(q);" in "keyup". I only passed it there as it would split-out "NaN" instead of a number to countdown from.
JS:
wordCount();
$('#group_3_1').click(function(){
var spliced = 200;
wordCount(spliced);
}) ;
$('#group_3_2').click(function(){
var spliced = 600;
wordCount(spliced);
}) ;
function wordCount(q) {
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0)
var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
$('.message1').keyup(function() {
wordCount(q);
});
try
{
if (new Number( word_count ) < 0) {
$(".word_count").attr("id","bad");
}
else {
$(".word_count").attr("id","good");
}
} catch (error)
{
//
}
};
HTML:
<input type="checkbox" name="entry.3.group" value="1/6" class="size1" id="group_3_1">
<input type="checkbox" name="entry.3.group" value="1/4" class="size1" id="group_3_2">
<div id="entry.8.single" class="message1" style="height: 400px; overflow-y:scroll; overflow-x:hidden;" contenteditable="true"> </div>
<span class="word_count" id="good"></span>
Thanks in advanced!
This is causing an infinite loop if (new Number(word_count) < 0) {.
Your code is a mess altogether. Just study and start with more basic concepts and start over. If you want to describe your project to me in a comment, I would be glad to show you a good, clean, readable approach.
Update:
Part of having a good architecture in your code is to keep different parts of your logic separate. No part of your code should know about or use anything that isn't directly relevant to it. Notice in my word counter that anything it does it immediately relevant to its word-counter-ness. Does a word counter care about what happens with the count? Nope. It just counts and sends the result away (wherever you tell it to, via the callback function). This isn't the only approach, but I just wanted to give you an idea of how to approach things more sensefully.
Live demo here (click).
/* what am I creating? A word counter.
* How do I want to use it?
* -Call a function, passing in an element and a callback function
* -Bind the word counter to that element
* -When the word count changes, pass the new count to the callback function
*/
window.onload = function() {
var countDiv = document.getElementById('count');
wordCounter.bind(countDiv, displayCount);
//you can pass in whatever function you want. I made one called displayCount, for example
};
var wordCounter = {
current : 0,
bind : function(elem, callback) {
this.ensureEditable(elem);
this.handleIfChanged(elem, callback);
var that = this;
elem.addEventListener('keyup', function(e) {
that.handleIfChanged(elem, callback);
});
},
handleIfChanged : function(elem, callback) {
var count = this.countWords(elem);
if (count !== this.current) {
this.current = count;
callback(count);
}
},
countWords : function(elem) {
var text = elem.textContent;
var words = text.match(/(\w+\b)/g);
return (words) ? words.length : 0;
},
ensureEditable : function(elem) {
if (
elem.getAttribute('contenteditable') !== 'true' &&
elem.nodeName !== 'TEXTAREA' &&
elem.nodeName !== 'INPUT'
) {
elem.setAttribute('contenteditable', true);
}
}
};
var display = document.getElementById('display');
function displayCount(count) {
//this function is called every time the word count changes
//do whatever you want...the word counter doesn't care.
display.textContent = 'Word count is: '+count;
}
I would do probably something like this
http://jsfiddle.net/6WW7Z/2/
var wordsLimit = 50;
$('#group_3_1').click(function () {
wordsLimit = 200;
wordCount();
});
$('#group_3_2').click(function () {
wordsLimit = 600;
wordCount();
});
$('.message1').keydown(function () {
wordCount();
});
function wordCount() {
var text = $('.message1').text(),
textLength = text.length,
wordsCount = 0,
wordsRemaining = wordsLimit;
if(textLength > 0) {
wordsCount = text.replace(/[^\w ]/g, '').split(/\s+/).length;
wordsRemaining = wordsRemaining - wordsCount;
}
$('.word_count')
.html(wordsRemaining + " words remaining...")
.attr('id', (parseInt(wordsRemaining) < 0 ? 'bad' : 'good'));
};
wordCount();
It's not perfect and complete but it may show you direction how to do this. You should use change event on checkboxes to change wordsLimit if checked/unchecked. For styling valid/invalid words remaining message use classes rather than ids.
I think you should use radio in place of checkboxes because you can limit 200 or 600 only at a time.
Try this like,
wordCount();
$('input[name="entry.3.group"]').click(function () {
wordCount();
$('.word_count').html($(this).data('val') + " words remaining...");
});
$('.message1').keyup(function () {
wordCount();
});
function wordCount() {
var q = $('input[name="entry.3.group"]:checked').data('val');
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0) var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
try {
if (Number(word_count) < 0) {
$(".word_count").attr("id", "bad");
} else {
$(".word_count").attr("id", "good");
}
} catch (error) {
//
}
};
Also you can add if your span has bad id then key up should return false;
See Demo

Find out the 'line' (row) number of the cursor in a textarea

I would like to find out and keep track of the 'line number' (rows) of the cursor in a textarea. (The 'bigger picture' is to parse the text on the line every time a new line is created/modified/selected, if of course the text was not pasted in. This saves parsing the whole text un-necessarily at set intervals.)
There are a couple of posts on StackOverflow however none of them specifically answer my question, most questions are for cursor position in pixels or displaying lines numbers besides the textarea.
My attempt is below, it works fine when starting at line 1 and not leaving the textarea. It fails when clicking out of the textarea and back onto it on a different line. It also fails when pasting text into it because the starting line is not 1.
My JavaScript knowledge is pretty limited.
<html>
<head>
<title>DEVBug</title>
<script type="text/javascript">
var total_lines = 1; // total lines
var current_line = 1; // current line
var old_line_count;
// main editor function
function code(e) {
// declare some needed vars
var keypress_code = e.keyCode; // key press
var editor = document.getElementById('editor'); // the editor textarea
var source_code = editor.value; // contents of the editor
// work out how many lines we have used in total
var lines = source_code.split("\n");
var total_lines = lines.length;
// do stuff on key presses
if (keypress_code == '13') { // Enter
current_line += 1;
} else if (keypress_code == '8') { // Backspace
if (old_line_count > total_lines) { current_line -= 1; }
} else if (keypress_code == '38') { // Up
if (total_lines > 1 && current_line > 1) { current_line -= 1; }
} else if (keypress_code == '40') { // Down
if (total_lines > 1 && current_line < total_lines) { current_line += 1; }
} else {
//document.getElementById('keycodes').innerHTML += keypress_code;
}
// for some reason chrome doesn't enter a newline char on enter
// you have to press enter and then an additional key for \n to appear
// making the total_lines counter lag.
if (total_lines < current_line) { total_lines += 1 };
// putput the data
document.getElementById('total_lines').innerHTML = "Total lines: " + total_lines;
document.getElementById('current_line').innerHTML = "Current line: " + current_line;
// save the old line count for comparison on next run
old_line_count = total_lines;
}
</script>
</head>
<body>
<textarea id="editor" rows="30" cols="100" value="" onkeydown="code(event)"></textarea>
<div id="total_lines"></div>
<div id="current_line"></div>
</body>
</html>
You would want to use selectionStart to do this.
<textarea onkeyup="getLineNumber(this, document.getElementById('lineNo'));" onmouseup="this.onkeyup();"></textarea>
<div id="lineNo"></div>
<script>
function getLineNumber(textarea, indicator) {
indicator.innerHTML = textarea.value.substr(0, textarea.selectionStart).split("\n").length;
}
</script>
This works when you change the cursor position using the mouse as well.
This is tough because of word wrap. It's a very easy thing to count the number of line breaks present, but what happens when the new row is because of word wrap? To solve this problem, it's useful to create a mirror (credit: github.com/jevin). Here's the idea:
Create a mirror of the textarea
Send the content from the beginning of the textarea to the cursor to the mirror
Use the height of the mirror to extract the current row
On JSFiddle
jQuery.fn.trackRows = function() {
return this.each(function() {
var ininitalHeight, currentRow, firstIteration = true;
var createMirror = function(textarea) {
jQuery(textarea).after('<div class="autogrow-textarea-mirror"></div>');
return jQuery(textarea).next('.autogrow-textarea-mirror')[0];
}
var sendContentToMirror = function (textarea) {
mirror.innerHTML = String(textarea.value.substring(0,textarea.selectionStart-1)).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br />') + '.<br/>.';
calculateRowNumber();
}
var growTextarea = function () {
sendContentToMirror(this);
}
var calculateRowNumber = function () {
if(firstIteration){
ininitalHeight = $(mirror).height();
currentHeight = ininitalHeight;
firstIteration = false;
} else {
currentHeight = $(mirror).height();
}
// Assume that textarea.rows = 2 initially
currentRow = currentHeight/(ininitalHeight/2) - 1;
//remove tracker in production
$('.tracker').html('Current row: ' + currentRow);
}
// Create a mirror
var mirror = createMirror(this);
// Style the mirror
mirror.style.display = 'none';
mirror.style.wordWrap = 'break-word';
mirror.style.whiteSpace = 'normal';
mirror.style.padding = jQuery(this).css('padding');
mirror.style.width = jQuery(this).css('width');
mirror.style.fontFamily = jQuery(this).css('font-family');
mirror.style.fontSize = jQuery(this).css('font-size');
mirror.style.lineHeight = jQuery(this).css('line-height');
// Style the textarea
this.style.overflow = "hidden";
this.style.minHeight = this.rows+"em";
var ininitalHeight = $(mirror).height();
// Bind the textarea's event
this.onkeyup = growTextarea;
// Fire the event for text already present
// sendContentToMirror(this);
});
};
$(function(){
$('textarea').trackRows();
});
This worked for me:
function getLineNumber(textarea) {
return textarea.value.substr(0, textarea.selectionStart) // get the substring of the textarea's value up to the cursor position
.split("\n") // split on explicit line breaks
.map((line) => 1 + Math.floor(line.length / textarea.cols)) // count the number of line wraps for each split and add 1 for the explicit line break
.reduce((a, b) => a + b, 0); // add all of these together
};
Inspired by colab's answer as a starting point, this includes the number of word wraps without having to introduce a mirror (as in bradbarbin's answer).
The trick is simply counting how many times the number of columns textarea.cols can divide the length of each segment between explicit line breaks \n.
Note: this starts counting at 1.

Categories