How can I use document.write in setTimout without clearing previous text - javascript

I want to create a delay between two document.writes. I used setTimeout to do so, but once it executes, it writes over the previous text. I would like to be able to display text, wait some delay, the display some other text below it without erasing any previous text. This code is appending to an otherwise empty HTML file. Also, I haven't had any success with using <br> for this.
var numOfDice = prompt("How many dice?");
var numOfSides = prompt("How many sides on these dice?");
var rollDice = function(numOfDice) {
var rollResults = [];
for (var i = 0; i < numOfDice; i++) {
rollResults[i] = Math.floor((Math.random() * numOfSides) + 1);
}
return rollResults;
}
var printResults = function() {
var i = 0;
while (i < rollResults.length - 1) {
document.write(rollResults[i] + ", ");
i++;
}
document.write(rollResults[i]);
}
alert("Roll the dice!");
var rollResults = rollDice(numOfDice);
printResults();
setTimeout(function() {document.write("These numbers...")}, 1000);

First, take a look at this answer why you shouldn't be using document.write.
Then, after you understand why using document.write is bad practice, you can replace it with element.appendChild.
For more information about the function, visit the Mozilla Development Network.
Code example:
var numOfDice = prompt("How many dice?");
var numOfSides = prompt("How many sides on these dice?");
var rollDice = function(numOfDice) {
var rollResults = [];
for (var i = 0; i < numOfDice; i++) {
rollResults[i] = Math.floor((Math.random() * numOfSides) + 1);
}
return rollResults;
}
var printResults = function() {
var i = 0;
while (i < rollResults.length - 1) {
var node = document.createElement("p");
node.innerHTML = rollResults[i] + ", ";
document.querySelector("body").appendChild(node);
i++;
}
var node = document.createElement("p");
node.innerHTML = rollResults[i];
document.querySelector("body").appendChild(node);
}
alert("Roll the dice!");
var rollResults = rollDice(numOfDice);
printResults();
setTimeout(function() {
var node = document.createElement("p");
node.innerHTML = "These numbers...";
document.querySelector("body").appendChild(node);
}, 1000);
Note that this code will show each rolled dice on a new line, since I'm creating a new p element for every roll. If you want them to appear on the same line, create one p element, and then in the while loop, append the rollresult to that p elements `innerHTML´
.

Related

Why I get Indefined when displaying Img’s on page?

I have been playing with this code for a while and I was wondering why when I try to add img’s to the array on the js code makes the images appear on DOM but also makes a bunch of Undefined elements appear, How can I just make the 15 images appear without the undefined? Thanks
enter link description here
var previous = document.getElementById('btnPrevious')
var next = document.getElementById('btnNext')
var gallery = document.getElementById('image-gallery')
var pageIndicator = document.getElementById('page')
var galleryDots = document.getElementById('gallery-dots');
var images = ["https://exoplanets.nasa.gov/internal_resources/1763/",
"https://cdn.britannica.com/56/234056-050-0AC049D7/first-image-from-James-Webb-Space-Telescope-deepest-and-sharpest-infrared-image-of-distant-universe-to-date-SMACS-0723.jpg",
"https://assets.newatlas.com/dims4/default/ac389ce/2147483647/strip/true/crop/1620x1080+150+0/resize/1200x800!/quality/90/?url=http%3A%2F%2Fnewatlas-brightspot.s3.amazonaws.com%2Farchive%2Funiverse-expanding-acceleration-1.jpg",
"https://media.newyorker.com/photos/590966ee1c7a8e33fb38d6cc/master/w_2560%2Cc_limit/Nissan-Universe-Shouts.jpg",
"https://www.thoughtco.com/thmb/NY5k_3slMRttvtS7mA0SXm2WW9Q=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/smallerAndromeda-56a8ccf15f9b58b7d0f544fa.jpg",
"https://static.scientificamerican.com/sciam/cache/file/05B8482C-0C04-4E41-859DCCED721883D2_source.jpg?w=590&h=800&7ADE2895-F6E3-4DF4-A11F51B652E9FA88",
"https://qph.cf2.quoracdn.net/main-thumb-66277237-200-huqebnzwetdsnnwvysbxemlskpcxnygf.jpeg",
"http://www.pioneertv.com/media/1090/hero_shot_1080x720.jpg?anchor=center&mode=crop&width=600&height=400&rnd=133159257140000000",
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRSWFW1EpMNFM5-dbZEUUnzJkzT3KbUCeuhPHx_eseFCpPeX4Q_DIVPopjS0LeKVmKdQho&usqp=CAU",
"https://cdn.mos.cms.futurecdn.net/rwow8CCG3C3GrqHGiK8qcJ.jpg",
"https://static.wixstatic.com/media/917d103965314e2eacefed92edb6492c.jpg/v1/fill/w_640,h_356,al_c,q_80,usm_0.66_1.00_0.01,enc_auto/917d103965314e2eacefed92edb6492c.jpg",
"https://astronomy.com/~/media/A5B9B6CF36484AB9A6FFAE136C55B355.jpg",
"https://discovery.sndimg.com/content/dam/images/discovery/fullset/2022/9/alien%20planet%20GettyImages-913058614.jpg.rend.hgtvcom.616.411.suffix/1664497398007.jpeg",
"https://images.newscientist.com/wp-content/uploads/2017/06/21180000/planet-10-orange-blue-final-small.jpg?crop=16:9,smart&width=1200&height=675&upscale=true",
"https://images.hindustantimes.com/img/2022/07/20/1600x900/Viral_Instagram_Planet_Rainbow_Nasa_1658316556293_1658316573815_1658316573815.PNG"
]
for (var i = 0; i < 15; i++) {
images.push({
title: "Image " + (i + 1),
source: images[i]
});
}
var perPage = 8;
var page = 1;
var pages = Math.ceil(images.length / perPage)
// Gallery dots
for (var i = 0; i < pages; i++){
var dot = document.createElement('button')
var dotSpan = document.createElement('span')
var dotNumber = document.createTextNode(i + 1)
dot.classList.add('gallery-dot');
dot.setAttribute('data-index', i);
dotSpan.classList.add('sr-only');
dotSpan.appendChild(dotNumber);
dot.appendChild(dotSpan)
dot.addEventListener('click', function(e) {
var self = e.target
goToPage(self.getAttribute('data-index'))
})
galleryDots.appendChild(dot)
}
// Previous Button
previous.addEventListener('click', function() {
if (page === 1) {
page = 1;
} else {
page--;
showImages();
}
})
// Next Button
next.addEventListener('click', function() {
if (page < pages) {
page++;
showImages();
}
})
// Jump to page
function goToPage(index) {
index = parseInt(index);
page = index + 1;
showImages();
}
// Load images
function showImages() {
while(gallery.firstChild) gallery.removeChild(gallery.firstChild)
var offset = (page - 1) * perPage;
var dots = document.querySelectorAll('.gallery-dot');
for (var i = 0; i < dots.length; i++){
dots[i].classList.remove('active');
}
dots[page - 1].classList.add('active');
for (var i = offset; i < offset + perPage; i++) {
if ( images[i] ) {
var template = document.createElement('div');
var title = document.createElement('p');
var titleText = document.createTextNode(images[i].title);
var img = document.createElement('img');
template.classList.add('template')
img.setAttribute("src", images[i].source);
img.setAttribute('alt', images[i].title);
title.appendChild(titleText);
template.appendChild(img);
template.appendChild(title);
gallery.appendChild(template);
}
}
// Animate images
var galleryItems = document.querySelectorAll('.template')
for (var i = 0; i < galleryItems.length; i++) {
var onAnimateItemIn = animateItemIn(i);
setTimeout(onAnimateItemIn, i * 100);
}
function animateItemIn(i) {
var item = galleryItems[i];
return function() {
item.classList.add('animate');
}
}
// Update page indicator
pageIndicator.textContent = "Page " + page + " of " + pages;
}
showImages();
I checked your code and make it work with a small modification.
You are reusing the same array with the links of images and push inside the new object with the shape of { title, source }.
You just need to do this changes:
Change the name of your array of images. Something from images to arrayOfImages.
const arrayOfImages = ["https://exoplanets.nasa.gov/internal_resources/1763/", ...]
Declare an empty array before your first for loop. Make something like const images = []
On your for loop, instead of loop over the images variable, do it over the arrayOfImages variable.
const images = [];
for (var i = 0; i < 15; i++) {
images.push({
title: "Image " + (i + 1),
source: arrayOfImages[i]
});
}
With those changes, everything works for me.
Also, as a recommendation, try to avoid the var keyword. If you want more details about this, this answer is very helpful: https://stackoverflow.com/a/50335579/17101307
You can use Array#map to create a new array of objects from the array of URLS, then replace the original array.
images = images.map((x, i) => ({
title: "Image " + (i + 1),
source: x
}));

Google Apps Script - Typewriter Effect

I'm trying to add a sort of 'typewriter effect' on my google apps script for google docs. I want to make it type out text, in this case a wikipedia article, as if a user was typing it, so add a delay. Unfortunately the function appendText(), even if you use Utilities.sleep, it still just types the entire article out as soon as the script finishes. What function would I use to accomplish something like this?
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'myFunction')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
var response = UrlFetchApp.fetch('https://en.wikipedia.org/w/api.php?&format=json&action=query&generator=random&grnnamespace=0&prop=title&grnlimit=1');
var json = JSON.parse(response);
for (key in json.query.pages) {
var title = json.query.pages[key].title;
}
var url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext=&titles=' + title
var response2 = UrlFetchApp.fetch(url);
var json2 = JSON.parse(response2);
for (key in json2.query.pages) {
var content = json2.query.pages[key].extract;
}
//content = content.replace(/==.*==/, '====')
var all = title + '\n' + content;
text.appendText("Start\n");
Utilities.sleep(1000);
text.appendText(content);
}
You need to flush the document. The DocumentApp API does not have a flush method (like SpreadsheetApp) but you can still flush it by using saveAndClose and then re-opening the document (for example with document=DocumentApp.openById("myid")
saveAndClose is automatically called when a script finishes, but not after every change you make as Google batches those changes for performance.
https://developers.google.com/apps-script/reference/document/document
I have tried #ZigMandel suggestion. It appears to work but the text is being typed from the left out.
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'myFunction')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
var response = UrlFetchApp.fetch('https://en.wikipedia.org/w/api.php?&format=json&action=query&generator=random&grnnamespace=0&prop=title&grnlimit=1');
var json = JSON.parse(response);
for (key in json.query.pages) {
var title = json.query.pages[key].title;
}
var url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext=&titles=' + title
var response2 = UrlFetchApp.fetch(url);
var json2 = JSON.parse(response2);
for (key in json2.query.pages) {
var content = json2.query.pages[key].extract;
}
//format(content);
//var par1 = body.insertParagraph(0, title);
//par1.setAlignment(DocumentApp.HorizontalAlignment.CENTER);
var str = "Sphinx of black quartz, judge my vow."
var split = str.split("");
for (var i = split.length - 1; i >= 0; i--) {
text.appendText(split[i]);
DocumentApp.getActiveDocument().saveAndClose();
body = DocumentApp.getActiveDocument().getBody();
text = body.editAsText();
}
}
function format(txt) {
txt = '\n' + txt;
txt = txt.replace(/\===(.+?)\===/g, "").replace(/\==
(.+?)\==/g,"").replace(/\n+/g, "\n").replace(/\n/g, "\n" + " ");
return txt;
}
To get the script to print out each character with a delay between, you can put your sleep method into a for loop.
for(var i = 0; i < content.length; i++) {
text.appendText(content[i]);
Utilities.sleep(200);
}
I think this gives you the effect you are looking for.

Javascript/html, dynamic divs, individual href's from 2d array?

I'm really new to javascript from C# and i'm having a little trouble. I wrote this function to make adding menu's a bit easier on my site. It works well except I can't seem to give my div's an individual url, even though I can give them an individual innerHtml.
I've been stuck trying different things such as divs[i].location.url etc.. but I can't seem to have anything work. My current solution has each div link to /contact.html which I'm a little confused by.
function DrawMainMenu() {
var btns = [
["About", "/about.html"],
["Portfolio", "/portfolio.html"],
["Resume", "/resume.html"],
["Contact", "/contact.html"]
];
var numOfBtns = btns.length;
var divs = new Array(numOfBtns);
for (var i = 0; i < numOfBtns; i++) {
divs[i] = document.createElement("div");
divs[i].className = "menuBtn";
divs[i].innerHTML = btns[i][0];
divs[i].style.height = (30 / numOfBtns) + "%";
divs[i].style.lineHeight = 3.5;
var link = btns[i][1];
divs[i].addEventListener('click', function() {
location.href = link;
}, false);
document.getElementById("buttons").appendChild(divs[i]);
}
}
Thanks
The problem is that the variable link gets overwritten each iteration, so when the event handler it gets link, which is the string '/contact.html', since that was the last value given to it.
You can try setting onclick attribute to elements, which will store the variable in the attribute onclick. Therefore, it will have the old and correct value.
function DrawMainMenu() {
var btns = [
["About", "/about.html"],
["Portfolio", "/portfolio.html"],
["Resume", "/resume.html"],
["Contact", "/contact.html"]
];
var numOfBtns = btns.length;
var divs = new Array(numOfBtns);
for (var i = 0; i < numOfBtns; i++) {
divs[i] = document.createElement("div");
divs[i].className = "menuBtn";
divs[i].innerHTML = btns[i][0];
divs[i].style.height = (30 / numOfBtns) + "%";
divs[i].style.lineHeight = 3.5;
var link = btns[i][1];
divs[i].setAttribute('onclick', 'location.href = "' + link + '"');
document.getElementById("buttons").appendChild(divs[i]);
}
}
DrawMainMenu();
<div id="buttons"><div>
Updated answer
Here we make use of closures. Using a closure (closing the values of link) we bind the value to the scope of the click handler.
function DrawMainMenu() {
var btns = [
["About", "/about.html"],
["Portfolio", "/portfolio.html"],
["Resume", "/resume.html"],
["Contact", "/contact.html"]
];
var numOfBtns = btns.length;
var divs = new Array(numOfBtns);
for (var i = 0; i < numOfBtns; i++) {
(function() {
divs[i] = document.createElement("div");
divs[i].className = "menuBtn";
divs[i].innerHTML = btns[i][0];
divs[i].style.height = (30 / numOfBtns) + "%";
divs[i].style.lineHeight = 3.5;
var link = btns[i][1];
document.getElementById("buttons").appendChild(divs[i]);
divs[i].addEventListener('click', function() {
location.href = link;
}, false);
}());
}
}
DrawMainMenu();
<div id="buttons"><div>

Js function always return the same values

Js beginner here.
I have a function like this:
generateSteps: function() {
var stepsLength = this.data.steps.length;
var dataStepsInit = this.data.steps;
for (var i = 0; i < stepsLength; i++) {
var stepsItem = dataStepsInit[i].ITEM;
var arrayItem = this.animationNodes[stepsItem - 1];
var transition = this.animationParameters[i].transition;
var options = this.animationParameters[i].options;
var speed = this.animationParameters[i].speed;
var delay = this.animationParameters[i].delay;
arrayItem.delay(delay).show(transition, options, speed);
if (dataStepsInit[i].AUDIOID) {
var audioClass = dataStepsInit[i].AUDIOID;
var audioPlayer = this.template.find("audio." + audioClass);
setTimeout(playAudioOnDelay,delay);
};
var playAudioOnDelay = function() {
audioPlayer[0].pause();
audioPlayer[0].currentTime = 0;
audioPlayer[0].play();
};
}
}
What it does is generate data from JSON and display animated elements one by one on delay. Animation part work fine. I can assign required animations and delay to DOM elements and show them in right order.
But what I want to do in the same time is also to play an audio on delay (so I use setTimeout). Everything is almost fine, I play audio in right time (correct delay value) but I always play the same audio (which is last element) because audioPlayer always is the same DOM node.
I think this have something to do with this or I mixed a scope?
Try this:
generateSteps: function() {
var stepsLength = this.data.steps.length;
var dataStepsInit = this.data.steps;
for (var i = 0; i < stepsLength; i++) {
var stepsItem = dataStepsInit[i].ITEM;
var arrayItem = this.animationNodes[stepsItem - 1];
var transition = this.animationParameters[i].transition;
var options = this.animationParameters[i].options;
var speed = this.animationParameters[i].speed;
var delay = this.animationParameters[i].delay;
arrayItem.delay(delay).show(transition, options, speed);
if (dataStepsInit[i].AUDIOID) {
var audioClass = dataStepsInit[i].AUDIOID;
var audioPlayer = this.template.find("audio." + audioClass);
setTimeout(playAudioOnDelay(audioPlayer),delay);
};
}
function playAudioOnDelay(audioPlayer){
return function(){
audioPlayer[0].pause();
audioPlayer[0].currentTime = 0;
audioPlayer[0].play();
}
}
}
Essentially, your problem looks like this: http://jsfiddle.net/po0rLnwo/
The solution is : http://jsfiddle.net/gpfuo1s8/
Check the console in your browser.

Scroll text method, works with arrays, not with string

Basic javascript function to scroll the text in the title bar, I'm calling it via a setInterval("rotateTitle()", 1000); call after onload.
This function, which takes text from an array, works perfectly.
var counter = 0;
function rotateTitle() {
var baseTitle = "www.mydomain.com - now with JavaScript";
var titleArray = new Array("a","b","c","d","e","f","g");
var titleString = "abcdefg";
var scrollText = getNextScroll(titleArray);
window.document.title=baseTitle.concat(scrollText);
}
function getNextScroll(inValue) {
var str = " ";
for (var i = 0; i<inValue.length; i++) {
var index = i+counter;
if (i+counter >= inValue.length) {
index -= inValue.length;
}
str += inValue[index];
}
counter++;
if (counter > inValue.length) {
counter = 0;
}
return str;
}
Edited here for clarity:
Now if I rewrite the function to scroll a string (not an array), I change the line
str += inValue[index];
to
str.concat(inValue.charAt(index));
and change getNextScroll(titleArray) to getNextScroll(titleString), the script seems to execute, but only the baseTitle is shown.
Why is this wrong?
You have to assign the result of str.concat back to str; otherwise you'll miss the concat operation. Instead of charAt you must use inValue[index].
Do like this:
str = str.concat(inValue[index]);
Here's a JS Bin: http://jsbin.com/aCEBAju/2/
In your original code you have this:
str.concat(inValue.charAt(index));
debugging in Chrome it barks: array has no method charAt.
The solution to the problem is that str.concat(inValue.charAt(index)); must change to str = str.concat(inValue.charAt(index)); or str += inValue.charAt(index);. Str must be assigned the new value. This is the entire working function:
var counter = 0;
function rotateTitle() {
var baseTitle = "www.berrmal.com - now with JavaScript";
var titleArray = new Array("b","e","r","r","m","a","l"); //no longer necessary
var titleString = "berrmal: bigger, longer, uncut";
var scrollText = getNextScroll(titleString);
window.document.title=baseTitle.concat(scrollText);
}
function getNextScroll(inString) {
var str = " ";
for (var i = 0; i<inString.length; i++) {
var index = i+counter;
if (i+counter >= inString.length) {
index -= inString.length;
}
str += inString.charAt(index);
}
counter++;
if (counter > inString.length) {
counter = 0;
}
return str;
}
I figured out the answer to the problem based on Leniel Macaferi's answer, though his posted code is not correct. This method runs successfully in Firefox 23.0 with no error in the console.

Categories