How to fix my delayed loop which stuck after the 5 seconds? - javascript

I would like to create a Jscript which I would paste to the Google Chrome Consol, and it should copy the innerHTML part of an element to the clipboard.
This should be done in every for a long period of time.
So far I managed to write a code which loops from 0 to 9, calling the function which copies the content to the clipboard, then sleeps for 1 seconds. It also writes to the console the the number of loops and the data itself.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function mycopy() {
var copy = function (e) {
e.preventDefault();
var text = document.getElementsByClassName("md-tile")[0].innerHTML
text = text.substring(0,10)
console.log(text);
if (e.clipboardData) {
e.clipboardData.setData('text/plain', text);
} else if (window.clipboardData) {
window.clipboardData.setData('Text', text);
}
};
window.addEventListener('copy', copy);
document.execCommand('copy');
window.removeEventListener('copy', copy);
}
var text = "a"
for (let XYZ = 0; XYZ < 10; XYZ++) {
console.log('copy' + XYZ + ' Sec');
text = ""
mycopy();
await sleep(1000);
}
console.log('Done');
As far I understand with my limited knowledge, the above script should
copy the the inner HTML of the first element where the class is "md-title".
However it will stop copying exactly after 5 seconds. ( it is still 5 seconds if we change the sleep time.)
This is the response in the consol:
copy0 Sec
div class
copy1 Sec
div class
copy2 Sec
div class
copy3 Sec
div class
copy4 Sec
div class
copy5 Sec
copy6 Sec
copy7 Sec
copy8 Sec
copy9 Sec
Done
Is there any idea how to get around this?
Is there a time limit for reaching the clipboard after 5 seconds?
Is it a chrome limitation, or windows (7)?

So at the end I managed to solve my issue, however I was not able to use the clipboard as we loose access to it after 5 seconds.
My solution was that I downloaded the HTML part as txt.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + text);
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
for (let XYZ = 0; XYZ < 10; XYZ++) {
var text0 = document.getElementById('lastUpdate').innerHTML
download("DialogLiveLogTime.txt",text0);
await sleep(2000);
console.log('Round: ' + XYZ)
}
With the above code the Google chrome will download the inner HTML as txt file. Originally this would popup a Save As window, but you can turn that off in the Chrome options menu, plus you can set the destination there.
In the meantime it can be wise to delete the downloaded logs with the same frequency or after read, with a different script.

Related

When does a change to TextContent get displayed?

I'm working on Rock/Paper/Scissors game for the Odin Project. I display the result of a game in a div resultMsg, and a running tally based on that result in countMsg. Both those items are divs in the HTML and they work correctly. After a total of 5 games are won or lost, I want to (in order) clear the textContent messages, use an "alert" to give a final tally, and start over.
I expected that the two lines highlighted with "->" would clear the textContent message. However they do not until after I click [OK] to clear the alert. I'd really like to understand why that is.
The HTML relevant body:
<div id="playersChoice">
<button id='rock'>Rock</button>
<button id='paper'>Paper</button>
<button id='scissors'>Scissors</button>
</div>
<div id="result">
</div>
<div id="count">
</div>
The Javascript that's relevant.
let gamesPlayed = 0;
let playerWon = 0;
let computerWon = 0;
const countMsg = document.querySelector ('#count');
const resultMsg = document.querySelector ('#result');
// Get all the buttons within playerschoice container
const userButton = document.querySelectorAll ('#playersChoice > button');
// For each button, create an event listener for the click which will play a round.
// Note the Button.ID identifies the players choice.
userButton.forEach(button => {
button.addEventListener('click', function() {
gamesPlayed++;
resultMsg.textContent = playRound(button.id, computerPlay());
// if there are less than 5 clear wins or losses
if ((playerWon + computerWon) < 5) {
countMsg.textContent = "The current tally is your " + playerWon + " wins to the computers " + computerWon + ".";
} else {
// there have been 5 definitive games, declare the overall winner!
-> resultMsg.textContent = '';
-> countMsg.textContent = '';
gamesPlayed = 0;
playerWon = 0;
computerWon = 0;
alert("Best of 5 series results : You won " + playerWon +", lost " + computerWon + ", and tied "+ (5-playerWon-computerWon) + " of them.");
}
});
});
'''
alert() will block the current script execution, but it seems to also blocks the DOM update. That's why even though the assignement to textContent is before the alert, the text is only shown after the alert has been clicked, and the execution has resumed.
You can use a very small setTimeout to allow the DOM to update before the alert() fires:
const div = document.querySelector("div");
function test1() {
div.textContent = "Test without timeout!";
alert("Test1!");
}
function test2() {
div.textContent = "Test with timeout!";
setTimeout(() => alert("Test2!"), 10);
}
<button onclick="test1()">Test</button>
<button onclick="test2()">Test with timeout</button>
<h4>Text content:</h4>
<div></div>
Edit:
I researched a bit more, and to be more precise, DOM updates happen only after the script has finished. Since alert() blocks the current script, the DOM update will only happen after the alert has been dismissed.
This behavior can also be seen with the following snippet:
function wait(ms) {
var start = Date.now(),
now = start;
while (now - start < ms) {
now = Date.now();
}
}
function test() {
document.querySelector("div").textContent = "Test with delay!";
wait(2000);
}
<button onclick="test()">Test with delay</button>
<div></div>

Automatic file downloads limited to 10 files on Chrome browser

I have a webpage where we generate PDFs based upon the user selection of on-page items. This causes a postback (it's an ASP.NET WebForms page) which creates the PDFs server-side. An <a class="documentDownload"> tag is then added to the page for each item.
When the page reloads in the browser the following jQuery script is executed to automatically download the files (if the user had chosen a auto-download option):
var divHost = document.createElement("div");
divHost.id = "elmntDnldLinks";
divHost.style.display = "none";
document.body.appendChild(divHost);
setTimeout(function() {
$(".documentDownload").each(function(idx, val) {
var lnkDownload = $(val),
save = document.createElement("a");
save.href = lnkDownload.attr("href");
save.download = lnkDownload.attr("download");
save.target = "_blank";
divHost.appendChild(save);
save.click();
});
}, 1000);
This script has a delay of 1 second, then for each .documentDownload element it creates a new <a> element with the same href attribute of the original element, appends it to a newly-added hidden element, then programmatically clicks it.
[This strategy of creating new links and clicking those instead of clicking the original DOM elements gets around a browser security measure.]
This works perfectly well in Firefox but Chrome never downloads more than 10 files. Why? I can see, for example, 15 links on the page and in the hidden element, but only 10 files are downloaded.
If you pause for a second between each 10 downloads, all of them will work in Chrome.
I used async timeout function for this workaround:
function pause(msec) {
return new Promise(
(resolve, reject) => {
setTimeout(resolve, msec || 1000);
}
);
}
async function downloadAll(elements) {
var count = 0;
for (var e in elements) {
download(elements[e]); // your custom download code here, click or whatever
if (++count >= 10) {
await pause(1000);
count = 0;
}
}
}
Simple timeouts multiplied by element counter could probably work too, but I did not test it.
You can do a pause in your downloads like that
function sleep(milliseconds) {
let timeStart = new Date().getTime();
while (true) {
let elapsedTime = new Date().getTime() - timeStart;
if (elapsedTime > milliseconds) {
break;
}
}
}
$("#aDescarga")[0].click();
$("#aDescarga").attr("href","");
if (i > 9) {
sleep(2000);
i = 0;
} else {
i = i + 1;
}
Setting a 200ms delay between each download solves the problem.
Tested on Google Chrome 100.0.4896.127 (Windows, macOS)
Demo in CodePen - 'Download multiple files' should be allowed.
const downloadFileWithAnchor = () => {
const anchor = document.createElement("a");
anchor.href = "data:text/plain;charset=utf-8,";
anchor.download = 'blank.txt';
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
};
const repeatCount = 20;
for (let i = 0; i < repeatCount; i += 1) {
setTimeout(
() => {
downloadFileWithAnchor();
},
i * 200 // Delay download every 200ms
);
}

.appendChild and window.print();

I have the following code:
const printString = // a long string w/ several base64 encoded images;
const printContainer = document.createElement('div');
printContainer.innerHTML = printString;
document.body.appendChild(printContainer);
window.print();
printString is a long string with several largeish base64 encoded images included. The string gets set as the innerHTML of the printContainer and then the whole thing gets printed.
This works okay, but on the initial load of the page, it apparently takes the browser a moment to render the base64 encoded images and in that time, window.print() goes ahead and fires, before all the images have actually loaded into the DOM.
That is, window.print() can fire before .innerHTML has finished rendering the new element.
If I add a brief delay to the window.print(), then everything works fine. Like so:
const printString = // a long string w/ several base64 encoded images;
const printContainer = document.createElement('div');
printContainer.innerHTML = printString;
document.body.appendChild(printContainer);
setTimeout(() => {
window.print();
}, 100);
This isn't a great solution, however, and I would really like to find a solution along the lines of "you just wait until .innerHTML() is actually finished, window.print();
All of this is tested in Chrome, so far.
Any ideas appreciated!
Edit: an answer
This is a modest reworking of #Keith's answer below.
const imgs = document.querySelectorAll('img.images-in-question');
function checkDone() {
if (ready === imgs.length) {
// do stuffs
}
}
function incrementReady(){
ready++;
checkDone();
}
for (const img of imgs) {
if (img.complete) ready++;
else {
img.addEventListener('load', incrementReady);
}
}
checkDone();
Below is a simple script to wait for all images to load.
It basically does a querySelectAll to get all the images, and then attaches the onload event, when the amount of images loaded is equal to the amount of images in the list, everything is then loaded.
This then will of course work with both external URL, and data uri's..
Update, noticed a slight issue with my original image load check, in Chrome sometimes the onload is not fired, I assume it's because if it can pull the resources from cache, it might get loaded before the onload event is even attached, as such I've added a check for the complete flag first. This seems to fix the issue..
const imgs = document.querySelectorAll("img");
let waiting = 0;
let count = 0;
function checkDone() {
if (count === waiting) {
console.log("all images loaded");
}
}
for (const img of imgs) {
if (!img.complete) waiting ++;
else img.addEventListener('load', () => {
count ++;
checkDone();
});
}
checkDone();
<img src="data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" style="width:100px;height:100px">
<img src="https://via.placeholder.com/350x150.svg">
<img src="https://via.placeholder.com/300x100.svg">
<img src="https://via.placeholder.com/200x400.svg">
This would be a good case to use requestAnimationFrame.
const printString = // a long string w/ several base64 encoded images;
const printContainer = document.createElement('div');
printContainer.innerHTML = printString;
document.body.appendChild(printContainer);
requestAnimationFrame(function () {
window.print();
});
requestAnimationFrame waits for the next paint and then runs the function, thus you can be sure that window.print() won't run until just after the HTML has been rendered.

output specific data from div and print it via printer

I've spent all day in getting a solution for my need but since i'm not an experienced coder it's time for me to ask you guys for a little help.
My scenario is:
I have some php code like:
<div id="unique_name">
<?php function_show_qrcode()?>
<?php function_show_text1()?>
<?php function_show_text2()?>
<?php function_show_text3()?>
</div>
What i want to achieve is to print the content of "unique_name" div (qrcode and some additional info) directly to a label printer attached via wireless on my smartphone and PC, printer that prints on continuous paper with a width of 62mm.
I have tried a lot of codes that i found but with no success because of browsers behavior regarding window.print()
The following code is working on firefox and chrome on pc but it is not working on safari mobile
<script>
function printDiv(unique_name) {
var printContents = document.getElementById(unique_name).innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;
}
</script>
Print label
I am stuck on that and i think that something like converting the content of unique_name div into an image, save it and print it after that should work but there are a lot of steps to do and we want to do it more easy something like click and print or click and popup and then print and the most important thing is that it needs to work on mobile browsers
Thank you in advance!
try this :
<script>
function printDiv(myId){
var HiddenElements = document.querySelectorAll( 'body *' ); // get all the elements in the body
var VisibleElements = document.querySelectorAll( '#' + myId + ' *' ); // gets the elements inside the div to be printed
var index = 0, length = HiddenElements.length;
for ( ; index < length; index++) {
HiddenElements[index].style.visibility = "hidden"; // hide all the elements in the body
}
index = 0;
length = VisibleElements.length;
for ( ; index < length; index++) {
VisibleElements[index].style.visibility = "visible"; // show all the elements inside the div to be printed
}
// display the element to be printed
myElement = document.getElementById(myId);
myElement.style.visibility = "visible"
var oldPos = myElement.style.position;
myElement.style.position = "absolute";
myElement.style.left = 0;
myElement.style.top = 0;
setTimeout(window.print, 1000); // Wait a bit for the DOM then Print ( Safari :/ )
// wait for the data to be sent to the printer then display the previous content
setTimeout(function(){
index = 0;
length = HiddenElements.length;
for ( ; index < length; index++) {
HiddenElements[index].style.visibility = "visible";
}
myElement.style.position = oldPos;
}, 5000);
}
</script>
Print label
JsFiddle : https://jsfiddle.net/2m5ha1ta/67/
i don't know why on JsFiddle the onclick doesn't work, so i added an id to the a and added an eventListener to it and it's working ( check the fiddle )
i don't know why exactly but apparently in Safari , Window.print is executed before the DOM is manipulated,
try putting the window.print() in a timeout:
replace this line window.print(); with setTimeout(window.print, 1000);
give it some time to print, then put back the original content :)
setTimeout(function(){
document.body.innerHTML = originalContents;
}, 2000)
you can play with the timeout's number of milliseconds until you get the sweet spot, it doesn't have to be 2000.

How to insert pause in speech synthesis with grammatical hints

I am writing a simple spelling test app using the HTML5 SpeechSynthesis API. The text I would like my app to say is something like the following: "The spelling word is Cat. The cat chased the dog.".
The API tends to race without much of a pause from the first sentence to the second. I wonder if there is a way to insert a bit of a pause between the 2 sentences. I realize I could create 2 separate utterances and use the pause() call. However the code would be simpler and less brittle if I could simply insert grammatical hints.
Normally in spoken English, one tends to pause a little longer between paragraphs. So I inserted a newline character in my text, but there was no noticeable impact.
I also tried using an ellipsis.
Is there any way to do this or am I stuck breaking everything into separate utterances?
Using an exclamation point "!" adds a nice delay for some reason.
You can chain them together with periods to extend the pause.
"Example text! . ! . ! . !"
Split your text using comma (or custom delimiter) and add your own space using a timeout.
Here is a simple example as a proof-of-concept. Extending it, you can customize your text to include hints as to how long to pause.
function speakMessage(message, PAUSE_MS = 500) {
try {
const messageParts = message.split(',')
let currentIndex = 0
const speak = (textToSpeak) => {
const msg = new SpeechSynthesisUtterance();
const voices = window.speechSynthesis.getVoices();
msg.voice = voices[0];
msg.volume = 1; // 0 to 1
msg.rate = 1; // 0.1 to 10
msg.pitch = .1; // 0 to 2
msg.text = textToSpeak;
msg.lang = 'en-US';
msg.onend = function() {
currentIndex++;
if (currentIndex < messageParts.length) {
setTimeout(() => {
speak(messageParts[currentIndex])
}, PAUSE_MS)
}
};
speechSynthesis.speak(msg);
}
speak(messageParts[0])
} catch (e) {
console.error(e)
}
}
function run(pause) {
speakMessage('Testing 1,2,3', pause)
}
<button onclick='run(0)'>Speak No Pause</button>
<button onclick='run(500)'>Speak Pause</button>
<button onclick='run(1000)'>Speak Pause Longer</button>
Just insert
<silence msec="5000" />
in the text for 5 sec waiting (Source).
Disclaimer: This code works only in an appropriate user agent.
// code taken from https://richjenks.com/dev/speechsynthesis/
var utterance = new SpeechSynthesisUtterance(),
speak = document.getElementById("speak"),
text = document.getElementById("text");
// Delay links and events because speechSynthesis is funny
speechSynthesis.getVoices();
setTimeout(function () {
// Add event listeners
var voiceLinks = document.querySelectorAll(".voice");
for (var i = 0; i < voiceLinks.length; i++) {
voiceLinks[i].addEventListener("click", function (event) {
utterance.voice = speechSynthesis.getVoices()[this.dataset.voice];
});
}
}, 100);
// Say text when button is clicked
speak.addEventListener("click", function (event) {
utterance.text = text.value;
speechSynthesis.speak(utterance);
});
<textarea id="text" rows="5" cols="50">Hi <silence msec="2000" /> Flash!</textarea>
<br>
<button id="speak">Speak</button>
I’ve found inserting synthetic pauses using commas to be quite useful (as an making other manipulations). Here’s a little excerpt:
var speech = new SpeechSynthesisUtterance(),
$content = document.querySelector('main').cloneNode(true),
$space = $content.querySelectorAll('pre'),
$pause_before = $content.querySelectorAll('h2, h3, h4, h5, h6, p, li, dt, blockquote, pre, figure, footer'),
$skip = $content.querySelectorAll('aside, .dont_read');
// Don’t read
$skip.forEach(function( $el ){
$el.innerHTML = '';
});
// spacing out content
$space.forEach(function($el){
$el.innerHTML = ' ' + $el.innerHTML.replace(/[\r\n\t]/g, ' ') + ' ';
});
// Synthetic Pauses
$pause_before.forEach(function( $el ){
$el.innerHTML = ' , ' + $el.innerHTML;
});
speech.text = $content.textContent;
The key is to clone the content node first so you can work with it in memory rather than manipulating the actual content. It seems to work pretty well for me and I can control it in the JavaScript code rather than having to modify the page source.

Categories

Resources