My code has an animated GIF (loading image)
I get the html page in a variable "data" from Ajax call.
$("#content").html(data); // takes about 5 seconds and the GIF animation freezes since IE is single threaded.
Is there a way to write the data line by line with a setTimeout / setInterval function so that the thread is released for a brief moment so that the animation continues?
Fiddle URL:
http://jsfiddle.net/deepakssn/Kp2bM/
Alternate solution I tried:
<style>
#loading {
background: url(loading.gif) no-repeat center center #fff;
display: block;
position: fixed;
height: 500px;
width: 500px;
}
</style>
<script src="../scripts/jquery.js"></script>
<script>
$(window).load(function () {
function writeData(data) {
var yourLines = data.split("\n");
for (var i = 0, j = yourLines.length; i < j; i++) {
var x = setTimeout(function() {
$("#content").append(yourLines[i]);
console.log("Line No :"+[i]+" "+Date.now());
}, 5000);
}
}
function ajaxLoad() {
$.ajax({
url: "test.txt"
}).done(function (data) {
console.log("success"+Date.now());
writeData(data);
//$("#content").html(data);
console.log("loaded data"+Date.now());
});
}
ajaxLoad();
});
</script>
<div id="loading"></div>
<div id="content"></div>
I ran into this problem while adding a 'loading' indicator for one of my projects. I used this which solved the problem for me. Since it's not an image, it doesn't freeze.
Related
Kinda JS/dev newbie here. Having a play around with a loading spinner - this is the example I'm working from. I've currently got a bunch of JS calculations that are performed on a Flask/SQLite backend API. I'll ignore the CSS as it's likely irrelevant.
document.onreadystatechange = function() {
if (document.readyState !== "complete") {
document.querySelector("main").style.visibility = "hidden";
document.querySelector("#loader").style.visibility = "visible";
} else {
document.querySelector("#loader").style.display = "none";
document.querySelector("main").style.visibility = "visible";
}
};
This in the html:
<main role="main" class="container">
<div id="loader" class="spinner-1"></div>
...content here...
</main>
<script type="text/javascript" src="{{ url_for ('static', filename='scripts/main.js') }}"></script>
The issue is that the JS is still running on DOM load. So the spinner disappears and items are still being added to the DOM via JS. They appear after the spinner disappears, which negates the point of having a spinner!
I've tried several of these methods, but they all work the same.
I thought about having the conditional tied to one of the loading items, but that seems a bit clunky and I'd not be able to repeat the code on other pages on the site. Is there a baked in JS method for doing this properly?
EDIT - some of the JS I'm using
async function recipeGet () {
let response = await fetch('/recipeget/' + recipeId)
let data = await response.json();
return data
};
recipeGet();
async function efficiency () {
let mEfficiency = (await recipeGet()).efficiency;
mEfficiency = mEfficiency / 100
return mEfficiency
}
async function insertEff () {
let eff = await efficiency();
let effElement = document.querySelector('#eff');
effElement.innerText = (((Math.round(abv * 100) / 100 )) + "%");
};
insertEff();
I appreciate this may not be the right way to do things, but I'm still relatively new to developing, and it works currently.
ANSWER:
With the suggestion of the answer below, I managed to implement this:
JS
async function insertEff () {
let eff = await efficiency();
let effElement = document.querySelector('#eff');
effElement.innerText = (((Math.round(abv * 100) / 100 )) + "%");
document.querySelector("#loader").style.display = "none";
document.querySelector("#spins").style.visibility = "visible";
};
HTML
<div id="loader" class="spinner-1"></div>
<div class="tab-content" id="spins">
CSS
#spins {
visibility: hidden;
}
Where spins is the ID of the division I want to hide. It is initially hidden, then unhides when the function is executed. I still have to toy around with the HTML as the spinner jigs around a bit on page load, but that's a relatively trivial problem.
I would maybe change how you're receiving the data on your front end. It seems like you're trying to directly add the data from the back end to the front end using some kind of template.
Instead of doing this, try doing a fetch request from the front end after the page is loaded. That way you get the page much faster, and your spinner will be coordinated with the data you receive, meaning it will only disappear once you have all the data.
Here's an example of an ajax/fetch request:
const API_URL = 'https://opentdb.com/api.php?amount=3'
const dataWrapper = document.querySelector('.data-wrapper')
const spinner = document.querySelector('.spinner')
const data = document.querySelector('.data')
const fetchData = async (URL) => {
try {
const res = await fetch(URL)
const json = await res.json()
// populate data with json
data.innerText = JSON.stringify(json, undefined, 2)
spinner.remove() // or just make display: none; - whatever you need
} catch(err) {
console.log(err.message)
}
}
fetchData(API_URL)
.data-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.spinner {
position: absolute;
width: 100px;
height: 100px;
font-size: 100px;
color: blue;
animation: spin 1s linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
#keyframes spin {
to {
transform: rotate(360deg);
}
}
.data {
background: lightgray;
color: #235789;
font-family: 'Cascadia Code', monospace;
word-break: break-all;
white-space: pre-wrap;
width: 100%;
height: 100%;
overflow-y: scroll;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div class="data-wrapper">
<div class="spinner">
<i class="fas fa-spinner"></i>
</div>
<pre class="data"></pre>
</div>
The point is, by doing an ajax call in this way, the line where the spinner gets removed will only be reached and executed once the data is fully received.
window.onload = function() {
setTimeout(() => {
document.querySelector('#loader').style.opacity = '0';
setTimeout(() => {
document.querySelector('#loader').style.display = 'none';
document.querySelector('main').style.visibility = 'visible';
}, 200);
}, 1000);
}
I use this code to make my preloader smooth and hide after 1 second of window onload. Maybe this code will help you.
I am trying to figure out why MathJax render block gives me the wrong height for the div. The code is
<div class="text-field" id='inner-text'>\(\sqrt{b^{a}\frac{\partial y}{\partial x}}\)</div>
with CSS
.text-field {
display: inline-block;
overflow: hidden;
max-width: 15em;
}
When the following JS snippet is run
MathJax.typeset();
let text = document.getElementById("inner-text");
console.log(text.clientHeight,
text.offsetHeight,
text.getBoundingClientRect().height,
window.getComputedStyle(text).getPropertyValue('height'));
The console gives
41 41 41.25 "41.25px"
However, in inspect elements:
The actual height does not agree with any of height options accessible via JS. What is going on and how should can a get an accurate height value?
The problem is that it takes MathJax time to create the visualization. The idea of the solution I made is to give time to MathJax and when it is ready then we take the size of the element.
I made two versions of the code. Both work correctly in Firefox, Chrome, Edge... etc.
Option 1:
The script waits for MathJax to load then gives it another 100ms to complete and then takes the size of the inner-text
var checkEx = setInterval(function () {
let wrap = document.getElementById("inner-text");
var text = wrap.getElementsByClassName('MathJax')[0];
if (text) {
setTimeout(() => {
console.log(wrap.getBoundingClientRect().height, wrap.getBoundingClientRect().width);
}, 100);
clearInterval(checkEx);
}
}, 100);
.text-field {
display: inline-block;
overflow: hidden;
max-width: 15em;
}
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax#3/es5/tex-mml-chtml.js"></script>
<div class="text-field" id='inner-text'>\(\sqrt{b^{a}\frac{\partial y}{\partial x}}\)</div>
Option 2
The script waits for MathJax to load then begins to take the size of the element. When the size stops changing... return the size of the inner-text
var elhg;
var elwg;
var checkEx = setInterval(function () {
let wrap = document.getElementById("inner-text");
var text = wrap.getElementsByClassName('MathJax')[0];
if (text) {
elHeight = wrap.getBoundingClientRect().height;
elWidth = wrap.getBoundingClientRect().width;
if (elhg === elHeight && elwg === elWidth) {
console.log(elHeight, elWidth);
clearInterval(checkEx);
}
elhg = elHeight;
elwg = elWidth;
}
}, 100);
.text-field {
display: inline-block;
overflow: hidden;
max-width: 15em;
}
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax#3/es5/tex-mml-chtml.js"></script>
<div class="text-field" id='inner-text'>\(\sqrt{b^{a}\frac{\partial y}{\partial x}}\)</div>
Hello guys reading this article i create simple screensaver, but i got one problem, when i let my mouse stop i need to hide one div and show other, but when div shows animation stop, what is my problem, my code
var mousetimeout;
var screensaver_active = false;
var idletime = 5;
var screenSaver = $("#screenSaverForm");
var formDiv = $("#bodyForm");
function show_screensaver() {
formDiv.fadeOut(100);
screenSaver.fadeIn(900);
screensaver_active = true;
}
function stop_screensaver() {
screenSaver.fadeOut(100);
formDiv.fadeIn(900);
screensaver_active = false;
}
$(document).mousemove(function () {
clearTimeout(mousetimeout);
if (screensaver_active) {
stop_screensaver();
}
mousetimeout = setTimeout(function () {
show_screensaver();
}, 1000 * idletime); // 5 secs
});
and divs:
<div id="screenSaverForm" style="background-image: url(../../Content/img/screensavers.jpg); position: absolute; width: 100%; height:100%; left:0px; top: 0px; display: none; z-index:9999; display: none;">Example of a DIV element with a background image:</div>
Other div is simple, and if any can help, before show animation i need to reload page, any knows how to do this?
You could try adding this line in before stop_screensaver();
.
...
if (screensaver_active) {
location.reload(); //Refreshes the page
stop_screensaver();
}
...
.
Or, if you just want to scroll to the top of the page:
.
...
if (screensaver_active) {
$(window).scrollTop(0); //Scroll to top of page
stop_screensaver();
}
...
.
I want to display two images that I have no idea when they will be generated. So I want to use the jquery error function to keep checking if the images exist, and display each. Following code works on every browser except IE. Why does it not work on IE, really appreciate your help.
<style type="text/css">
DIV#loader {
width: 500px;
height: 500px;
overflow: hidden;
}
DIV#loader.loading {
background: url(spinner.gif) no-repeat center center;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
var image_names = new Array(2);
image_names[0] = 'a.jpg';
image_names[1] = 'b.jpg';
var divs = document.getElementsByTagName("div");
for ( var i = 0; i < divs.length; i++) {
showImage(image_names[i], divs[i]);
}
});
function showImage(src, div) {
var img = new Image();
$(img).load(function() {
$(this).hide();
$(div).removeClass('loading').append(this);
$(this).fadeIn();
}).error(function() {
setTimeout(function() {
$(img).attr('src', src);
}, 2000);
}).attr('src', src);
}
</script>
This is the HTML body
<body>
<h1>Image Loading</h1>
<div id="loader" class="loading"></div>
<div id="loader" class="loading"></div>
</body>
It seems like IE does not know the image is generated if it does not exist when the page first loads
It's the $(document).ready({}) part; throws an error in ie for me. Changing it to $(function(){}); seems to work for me at least in IE.
Caching caused the problem. Changed the src to:
$(img).attr('src', src + "?" + (new Date().getTime()));
I'd like to create an animation on a website to mimic a scrolling log file or tail -f. I'd feed it a list of fake log messages and they would be written to the bottom of the div and scroll up and off the top as new messages are displayed and then loop around. It needs to look authentic, white on black using a fixed width font etc.
Does anyone know of any javascript or jQuery libraries which could help me with this? I'm a beginner with javascript, so any advice on how to approach this would be much appreciated.
I've made a simple example for you
http://jsfiddle.net/manuel/zejCD/1/
// some demo data
for(var i=0; i<100; i++) {
$("<div />").text("log line " + i).appendTo("#tail")
}
// scroll to bottom on init
tailScroll();
// add button click
$("button").click(function(e) {
e.preventDefault();
$("<div />").text("new line").appendTo("#tail");
tailScroll();
});
// tail effect
function tailScroll() {
var height = $("#tail").get(0).scrollHeight;
$("#tail").animate({
scrollTop: height
}, 500);
}
#tail {
border: 1px solid blue;
height: 500px;
width: 500px;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="tail">
<div>some line of text</div>
</div>
<button>Add Line</button>
Here is a great solution
This uses an ajax request, and the HTTP Range: header to request only the last ~30KB of a log file. It then polls for data appended to that file, and only ever retrieves new data (no refreshing the whole file, or even the last 30KB). Handles file truncation too.
https://github.com/ukhas/js-logtail#readme
I've updated Manuel van Rijn's script to include a timer and a toggle switch, along with some minor changes to the log lines. hope this helps.
http://jsfiddle.net/5rLw3LoL/
html:
<div id="tail">
<div>some line of text</div>
</div>
<button>Add Line</button>
js:
var tailcounter = 100;
var tailswitch = false;
// scroll to bottom on init
tailScroll();
// add line to log
function tailappend() {
$("<div />").text("log line " + tailcounter).appendTo("#tail");
tailcounter++;
tailScroll();
}
// auto update every second
var t = setInterval(tailappend, 1000);
// toggle updates button click
$("button").click(function (e) {
e.preventDefault();
switch (tailswitch) {
case false:
clearInterval(t); // turns off auto update
tailswitch = true;
alert("auto update off");
break;
case true:
t = setInterval(tailappend, 1000); // restarts auto update
tailswitch = false;
alert("auto update on");
break;
}
});
// tail effect
function tailScroll() {
var height = $("#tail").get(0).scrollHeight;
$("#tail").animate({
scrollTop: height
}, 500);
}
css: (important for formatting)
#tail {
border: 1px solid blue;
height: 400px;
width: 500px;
overflow: hidden;
}
This can be achieved with CSS by simply flipping the outer and inner container using transform: rotateX(180deg); https://jsfiddle.net/tnrn6h59/2/
Only issue here is that the scroll is also reversed, not an issue for mobile.