I'm working on a Javascript/jQuery powered image preloader, and have hit a bit of a snag. While as of currently it provides the progress based on loaded_images / total_images, this is not very accurate given a page could have a thousand 1kB images, and a single 1MB image.
I'm looking for a way to incorporate filesize into the progress calculations. Now, I've looked into some (cross browser compatible) tricks at capturing the filesize of a given image, and it seems that Ajax requests for Content-Length were the most reliable (in terms of accuracy) like so:
var imageSizeTotal = 0;
var ajaxReqest = $.ajax({
type: 'HEAD',
url: 'path/to/image',
success: function(message){
imageSizeTotal += parseInt(ajaxRequest.getResponseHeader('Content-Length'));
}
});
Now, I find this method to be quite useful, as I can provide a status message of Initializing while the necessary requests are taking place. However my issue now is two-fold:
Is there any way possible to capture the bytes loaded of a given image object, perhaps using setInterval() to periodically check? Otherwise, I'm sort of back at the issue of the progress indicator hanging on large files.
How can I force the actual progress calculator, etc., portion of the script to wait until the necessary Ajax requests are completed (displaying Initializing or whatever), so it can go ahead with the loading?
Also, here's the script I currently use, which again, calculates progress based on the number of images, regardless of filesize or bytes received.
var preloaderTotal = 0;
var preloaderLoaded = 0;
var preloaderCurrent = null;
$('#preloaderCurtain')
.bind('preloaderStart', function(){
$(this)
.show();
$('*')
.filter(function(e){
if($(this).css('background-image') != 'none'){
preloaderTotal++;
return true;
}
})
.each(function(index){
preloaderCurrent = new Image();
preloaderCurrent.src = $(this).css('background-image').slice(5, -2);
preloaderCurrent.onload = function(e){
preloaderLoaded++;
if(preloaderLoaded == preloaderTotal - 1){
$('#preloaderCurtain')
.trigger('preloaderComplete')
}
$('#preloaderCurtain')
.trigger('preloaderProgress')
};
});
})
.bind('preloaderComplete', function(){
$(this)
.fadeOut(500)
startAnimation();
})
.bind('preloaderProgress', function(e){
$('#preloaderProgress')
.css('opacity', 0.25 + (preloaderLoaded / preloaderTotal))
.text(Math.floor((preloaderLoaded / preloaderTotal) * 100) + '%');
})
.trigger('preloaderStart');
Hopefully I'll be able to turn this into a plugin, once I work the bugs out of it.
It looks like a similar question was asked and answered here:
XmlHttpRequest.responseText while loading (readyState==3) in Chrome
and here:
Comet Jetty/Tomcat, having some browser issues with Firefox and Chrome
Basically - .responseText.length for Firefox and iPhone, .responseBody.length for IE, WebSockets for Chrome.
The second thread suggests bayeux/dojo encapsulate all this for you into a higher-level API so you don't have to write it yourself.
Related
I'm trying to make a website load forever. My current idea is to request a PHP file:
<?php
sleep(30);
This will delay the load by 30 seconds, which a quick Google search tells me should be within most browsers' timeouts. I was thinking of writing some JavaScript to append a new link tag after a bit less than 30 seconds to keep the page loading, but I found that this didn't keep the loading icon spinning (with Chrome at least):
window.addEventListener( 'load', () => {
var i = 0;
setInterval( () => {
i++;
var newScript = document.createElement('script');
newScript.src = 'infinite-loading.php?i=' + i;
document.querySelector('#infinite-loading').after(newScript);
console.log('The deed is done');
}, 25000)
} )
<script id="infinite-loading" src="infinite-loading.php"></script>
The code above appends a script tag every 25 seconds, and the browser loads the PHP file each time, but it doesn't show the loading icon. I added the URL parameter because I wasn't sure if browsers would cache the page.
I also want to make sure that the server with the PHP file won't be overloaded. I'm not sure if many sleep() functions running constantly at the same time will cause any issues.
Is there a better way to do this client-side? Should I use something other than PHP? Something multi-threaded?
(Edit: Sorry for the awkward title, Stack Overflow didn't like my first one.)
You need that browser will continue reading your page forever (I'm talking about HTML, not other linked objects). So you need not to break timeout and feed some data from backend to frontend.
Example of sending portion of data to client:
ob_end_flush();
# CODE THAT NEEDS IMMEDIATE FLUSHING
ob_start();
Now we need to understand the minimum data packet size that is expected by the browser. Minimal googling tells us a limit of 8-10 bytes.
So combining this together we can try to check (I did not checked, it is just my version):
<?php
while (true) {
sleep(25);
ob_end_flush();
echo " "; // 10 spaces...
ob_start();
}
Not sure why you would want to do anything like this but the simplest solution I think is an endless loop.
<?php
while(true)
{
}
I made an HTML5 player that uses <audio> tags.
All the audio controllers such as currentTime, duration, volume work great.
However I have a problem with showing the audio size.
The audio files is located in the same server where the php script reside.
I wrote such function:
$(function() {
$.ajax('<?php echo $myfile; ?>', {
type: 'HEAD',
success: function(d, r, xhr) {
myfile.innerText = "(" + xhr.getResponseHeader('Content-Length') / Math.pow(1024,2) + " MB)";
}
});
});
Problem 1: The filesize appears in this format (4.98463249206543 MB). I tried to add also toFixed(2), but it doesn't change. I want it to have it no decimal numbers, or at maximum 2 decimals.
Problem 2: the above size appears often. But not always, sometimes when I refresh the browser I get the size of a fragment of second and then appears undefined.
Is there a way I can fix those problems?
Or do it in a completely different way? PHP filesize doesn't seem to work. So I would stick with javascript... or maybe nodejs?
Just for fun, i'm trying to implement a "15 puzzle", but with 16 images (from 1 music photo) instead.
The thing is split into 2 scripts / sides. 1 Python CGI script that will perform the Last.FM query + splitting the image in Y x Z chunks. When the python script finishes it outputs a JSON string that contains the location (on server), extension etc.
{"succes": true, "content": {"nrofpieces": 16, "size": {"width": 1096, "height": 961}, "directoryname": "Mako", "extension": "jpeg"}}
On the other side is a HTML, JS, (CSS) combo that will query the CGI script for the images.
$(document).ready(function () {
var artiest = $("#artiest")
var rijen = $("#rijen")
var kolommen = $("#kolommen")
var speelveld = $("#speelveld")
var search = $("#search")
$("#buttonClick").click(function () {
var artiestZ = artiest.val()
var rijenZ = rijen.val()
var kolommenZ = kolommen.val()
$.getJSON("http://localhost:8000/cgi-bin/cgiScript.py", "artiest=" + artiestZ + "&rijen=" + rijenZ + "&kolommen=" + kolommenZ, function (JsonSring) {
console.log("HIIIIII")
if (JsonSring.succes === true){
console.log(JsonSring)
var baseUrl = "http://localhost:8000/"
var extension = JsonSring.content.extension
var url = baseUrl + JsonSring.content.directoryname + "/"
var amountX = rijenZ
var amountY = kolommenZ
for (var i = 0; i < amountX; i += 1){
for (var p = 0; p < amountY; p += 1){
console.log("HI")
var doc = new Image
doc.setAttribute("src", url + JsonSring.content.directoryname + i + "_" + p + "." +extension)
document.getElementById("speelveld").appendChild(doc)
}
}
}else {
// Search failed. Deal with it.
}
})
})
})
where the various id's link to various HTML elements. (Text Fields & Buttons & Div's).
Beneath is a screenshot of the full folder that contains the image files.
Now, coming to the point. All the HTML img tags with src seem correct, yet. Some images don't load, yet other do. I also noticed that all images failed to load in 2s intervals. Is there some kind of timeout, or so?
All this is being ran from a local machine, so disk speed and cpu shouldn't really affect the matter. Also, from what I understand: The call for making the img tags etc is done in a callback from the getJson, meaning it'll only run when getJson has finished / had a reply.
Does the great StackOverFlow community have an idea what's happening here?
To share my knowledge/experiences with the great StackOverflow community,
Small backstory
After progressing a bit further into the project I started to run into various issues going from JSON parsing to not having Allow-Control-Allow-Origin: * headers, making it very hard to get the Ajax Request (Client ==> Python CGI) done.
In the meantime I also started dev'ing on my main desktop (which for some reason either has massive issues with Python versioning or has none). But due to the terminal on my desktop having Python 3.4+ , there was no module CGIHTTPServer. After a small amount of digging, I found that CGIHTTPServer had been transfered into http.server, yet when running plain old python -m http.server, I noticed the CGI script wouldn't run. It would just display. Ofcourse, I forgot the option -cgi.
Main solution
The times I was succesfully using CGIHTTPServer, I had troubles. The images wouldn't load as described above. I suspect that the module just couldn't take the decent amount of requests. Meaning that when suddenly Y x Z requests came in, it would struggle to deliver all the data. ==> Connection Refused.
Since switching to python -m http.server -cgi, no problems what so ever. Currently working on a Bootstrap Grid for all those images!
Thx #Lashane and #Ruud.
This question already has answers here:
Sequencing ajax requests
(10 answers)
Closed 7 years ago.
I'm working on a small text game in js, and the easiest way I found to have save text is to use text files. I can order them in different folders, they really light and they're easily identifiable and editable in case I need to make changes.
I'm loading them using ajax
$.ajax({
url: 'text/example.txt',
success: function(text){
document.getElementById("main").innerHTML = document.getElementById("main").innerHTML + text;
}
});
As it was suggested to me in another thread.
And honestly, so far it's been working pretty well, in single-cases scenarios. When only one TXT file needs to be displayed there are literally no problems. But, unfortunately in cases where a lot of different files need to be displayed in a correct order (let's say around 10 different files), the text gets messed up and loads out of order. I'm going to suppose this is because it just can't fetch the txt file fast enough.
So at this point I'm really not too sure what to do.
Is there a way to get my script to wait before printing the next piece of text before displaying one that still hasn't loaded?
Maybe a way to load all the txt files when the site is accessed?
My knowledge is pretty limited so I'm really not sure how this could be fixed.
Tried searching google and stackoverflow, none of the threads I found are helping me, perhaps because I'm really not an expert.
You can achieve with callback, the following way will call ajax one by one after they finish:
//setup an array of AJAX url
var ajaxes = [{ url : 'text/example.txt'}, { url : 'text/example1.txt'}],
current = 0;
//declare your function to run AJAX requests
function do_ajax() {
//check to make sure there are more requests to make
if (current < ajaxes.length) {
//make the AJAX request with the given data from the `ajaxes` array of objects
$.ajax({
url : ajaxes[current].url,
success : function (text) {
document.getElementById("main").innerHTML = document.getElementById("main").innerHTML + text;
//increment the `current` counter and recursively call this function again
current++;
do_ajax();
}
});
}
}
//run the AJAX function for the first time when you want
do_ajax();
I am developing a website with a full background video.
To optimize for low speed connections / mobile, I am using a media query to detect screen sizes smaller then 768 px, then doing a display:none on the video container and displaying a background image instead.
My question here is:
Is this the correct way to optimize for low speed connections / mobile?
Will it have any impact on my optimization when not displaying containers with css or should I be doing it in JavaScript instead, when loading the page?
Media queries will allow you to load different images if they are set as backgrounds, so that's a start for small screens, but not for low speed on a computer, and it won't work in the case of a video, or additionnal files being loaded or not.
In JS
This is what I can think of at the moment, probably not very reliable, because it depends on how much content you have on your website.
It would consist in only having the most important stuff loaded (low speed connexion), and getting an approximate loading time for the content (DOM, images, css, js...). Then you can choose to either load the rest or not.
// get the current time as soon as you can (directly in the head tag)
var start = new Date().getTime();
// do the same after the page has loaded and find out the difference
window.onload = function(){
var end = new Date().getTime();
var timeTaken = end - start;
alert('It took ' + timeTaken + ' ms to load');
if(timeTaken < 2000){
// load more stuff if it took less than 2 seconds, for example
}
}
Again: not very reliable (a page with lots of images is going to take longer, and finding the perfect "timeout" (2 seconds here) won't be easy. Also, this won't work is your users have JS disabled, but that's not a concern I'm worried about these days :) You should probably wait for other answers.
In PHP
Another method I can think of is doing it in PHP if that's an option for you. You could have your php page get the time of its request by the client. Then for example if you have an external JS, you can do this:
index.php
<script src="myScript.php?time=<?=microtime()?>"></script>
myScript.php would be a php page that will get the time of this request, compare it with the first one , and then you can choose to serve different JS files based on that (That is called a proxy page).
From the JS file you choose, you can load different stuff based on what you want to do.
myScript.php
<?php
header("Content-Type: text/javascript");
$start = intval( $_GET['time'] );
$end = microtime();
$timeTaken = $end - $start;
if( $timeTaken < 2000 ){
echo file_get_contents('JSForHighSpeed.js');
} else {
echo file_get_contents('JSForLowSpeed.js');
}
?>
What are you using as a player for your videos?
For what you're doing, the answers will be in jQuery, not CSS. With videos, it's important to know what the user's bandwidth is so that you can supply the correct video resolution. Most phones can support 1080p resolutions (often times double, especially with Apple's Retina Display, or Samsung's 5K screens). In other words, it shouldn't matter if they are using a phone or a cinema display; what matters is their connection speed.
I've had good luck with JWPlayer and using Amazon S3 for storage. It's also been my experience that H.264 MP4's are the way to go.
Whatever you're using, you should be able to supply multiple versions of your video(s). For example, you might create different resolutions - 360, 720 and 1080.
Here's a jQuery utility you can use to determine the user's bandwidth. Make sure to create a file named "10.kb.file.zip" (and make sure it's exactly 10 kb).
/*
* measureBandwidth.js
* Directory: ~/lib/js/
* jQuery utility for measuring a user's bandwidth
*/
var url = 'js/10.kb.file.zip?{0}';
var start = '';
function getBandwidth(callback) {
start = new Date();
getFile(1, callback);
}
function getFile(i, callback) {
$.get(url.f(Math.random()), function () {
i++;
if (i < 6) {
getFile(i, callback);
} else {
var end = new Date();
var speed1 = Math.round(((50 / ((end - start) * .001) * 8) / 1000) * 10) / 10;
var speed2 = Math.round(50 / ((end - start) * .001) * 10) / 10;
callback(speed1, speed2);
}
});
}
String.prototype.f = function () { var args = arguments; return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; }); };
Then, you can use it like this:
getBandwidth(function (Mbits, kBs) {
$('#speed1').html(Mbits + ' Mbit/s');
$('#speed2').html(kBs + ' kB/s');
});
Based on those results, you can then set the appropriate video for the user.
For example, I route kBs < 128 to default to 360p video, and kBs > 128 to the 720p video.
In JWPlayer, you would add all of your videos to the "playlist" and give them labels like "360p", "720p" etc.