How to implement `using` in javascript? - javascript

I would like to make something like this (similar to c#):
using("content/jquery.js");
$("div").fadeOut();
So if content/jquery.js is not on a script in the head I would like to load it and continue the execution after it loads.
Is it possible to implement this or something similar? Note: I could have more than 1 using

These are called script loaders.
You can take a look at RequireJS, which behaves in a very similar way:
require(["helper/util"], function() {
//This function is called when scripts/helper/util.js is loaded.
});
There's also LabJS and ControlJS, which are more focused on async script loading rather than dependencies, but may be worth checking out. Also in this category, our very own #jAndy's SupplyJS.

You can use getScript:
$.getScript('test.js', function() {
alert('Load was performed.');
});

If you know the name of the functions you'll be calling ahead of time you can use typeof to check for their presence:
if (typeof($) == 'function') {
// Code here that uses jQuery
} else {
// Something else here that loads the script.
}
If you're not certain what functions you'll be using you probably want to retrieve a list of the script elements and check them. Something more along these lines:
var scripts = document.getElementsByTagName('script');
for (var i = 0, l = scripts.length; i < l; ++i) {
if (scripts[i].src == myVal) {
// do something here
} else {
// wait for the script to load, or just leave off the else.
}
}
Are the two approaches I can think of off the top of my head.

Related

What is the simplest way to alert when the DOM is finished painting new elements?

I just want to disable certain buttons and show a loading spinner until images are loaded.
This is a similar question- How to detect when an image has finished rendering in the browser (i.e. painted)?
I am new to javascript and am confused why there is not a simple way to determine this! Something similar to this-
document.addEventListener('readystatechange', event => {
if (event.target.readyState === 'complete') {
alert('complete');
}
});
Unfortunately after much searching it seems I will need to use a callback or a promise. I figured it would be a good time to learn about promises/async/await. I am having difficulty attempting to rewrite my functions using promises.
$('#container').on('click', '#imgSearch', function (e) {
$(this).html(`
<i class="fa fa-spinner fa-spin"></i>Loading`);
//various if statements here to check radio buttons
showImgs(search, sort, phpController, limit);
}
function showImgs(search, sort, phpController, limit) {
imgArray = [];
var a1 = $.post(phpController, {search: search, sort: sort, limit: limit}, {dataType: 'json'});
$.when(a1).done(function (response) {
if (response['error']) {
alert(response.error.img);
} else {
var imgs = response;
if (Array.isArray(imgs)) {
for (let i = 0; i < imgs.length; i++) {
//I use setTimeout here to render one image per second
setTimeout(function () {
offset++;
saveImages(imgs[i]);
//calling alertFinished on last img is my temporary solution to removing spinner after render
if (i === imgs.length - 1) {
alertFinished();
}
}, 1000 * i);
}
} else {
saveImages(imgs);
}
}
});
}
;
I use the saveImages function to empty and push to another array as well as other purposes not shown-
function saveImages(img) {
imgArray = [];
imgArray.push(img);
displayImages(imgArray);
}
;
displayImages renders the image while adding classes etc.-
function displayImages(imgs) {
var counter = 0;
imgs.forEach(img => {
var html = '';
html += `<div class="" id='${img.id}'><img src=${img.image}></div>`;
$(html).hide().appendTo(imgSection).fadeIn(1000);
});
}
;
AlertFinished removes the loading spinner from search button.
function alertFinished() {
$('#imgSearch').text('Search');
}
Will someone please explain how I can use promises/async/await in this context? It's unfortunate there isn't a way to use an event listener similar to readystatechange because the alternative is to refactor every function that renders new elements in the DOM in order to disable buttons, show spinner etc. Showing a spinner/loading msg is such a widespread feature I am surprised I am having difficulty implementing it appropriately.
So first off, as you can see from the question you linked to (and comments therein), there isn't a great way to tell when an image is actually painted. So I'll focus on a way you can call some function after images are loaded.
For starters, if you have a bunch of images in your page right as it first loads, and you just want to wait for them to load before doing something, you could try the super simple route which would be the window load event.
window.addEventListener('load', (event) => {
// do stuff
});
But I get the impression you have a situation where you're adding images dynamically and want to do something after they're loaded, so that won't work. Still, you're overcomplicating things, I think. A quick look at your function shows you're calling saveImages and displayImages inside a loop even though they appear to be things you want to do only once after you're done adding images.
Assuming that at some point in your whole process you find yourself with a bunch of images that have been added to your DOM, at least some of which are still in the middle of loading, what you need to do is check for the last image to be loaded and then remove your loading spinner afterwards.
The trick here is figuring out which image is last to load. It won't necessarily be the last one you added because images added earlier on could be larger.
One approach you can use to skip the whole promise/async confusion all together would be to use a recursive function that every so often checks whether all images are loaded and if not waits a bit and then checks again. If you make the wait before the function calls itself again relatively short, this will likely work just fine for you.
Something along these lines:
const registeredImages = [];
// add your images
for (let i = 0, l = someImages.length; i < l; i += 1) {
doSomething(); // add someImages[i] to DOM where you want it
someImages[i].setAttribute('data-id', `image-${i}`); // or whatever way to keep track of it
someImages[i].addEventListener('load', register);
}
// remove spinner once they're loaded
tryToRemoveSpinner();
function register(event) {
images.push(event.target.getAttribute('data-id');
}
function tryToRemoveSpinner {
if (registeredImages.length === someImages.length) {
removeSpinner(); // or whatever
} else {
setTimeout(() => { tryToRemoveSpinner() }, 100);
}
}
An enhancement you could add here would be to put some kind of counter on tryToRemoveSpinner so if some image fails to load for whatever reason it eventually just bails out or runs removeSpinner() anyway or whatever you want to do in the event of an error, just in case.
Related reading: How to create a JavaScript callback for knowing when an image is loaded

Loading external scripts async in JavaScript

This is my first module i'm writing in JS and I want to make sure i'm doing it right.
It is going to be a simple gallery module but I want it to be able to display youtube and vimeo movies also.
In my module I have function called init(). Before i'm displaying the gallery first I want to add all necessary libs, like youtube iframe api and vimeo js api.
only after they are loaded I want to display the gallery.
so my module looks something like this:
myModule = {};
myModule.isYoutubeApiLoaded = false;
myModule.isVimeoApiLoaded = false;
myModule.controller;
myModule.init = function (){
myModule.loadYoutubeapi();
myModule.loadVimeoApi();
myModule.startController();
}
now startController function looks like this:
myModule.startController = function (){
myModule.controller = setInterval(function(
if (myModule.isYoutubeApiLoaded && myModule.isVimeoApiLoaded ) {
myModule.buildGallery();
clearInterval(myModule.controller);
}
), 5);
}
in loadYoutubeapi() and loadVimeoApi() i'm setting the given flags when scripts are loaded.
is it a good approach?
No, it's not a good approach. It will load CPU and will possibly have unnecessary delay of 5 milliseconds.
Better way would be to add callbacks to loadYoutubeapi() and loadVimeoApi(). Once they finish they must call your new function (e.g. moduleHasLoaded()), which will count loaded modules. When all will be loaded you can call startController().
It will save CPU and will not have a unnecessary delay at all.
Something like this:
myModule = {};
myModule.isYoutubeApiLoaded = false;
myModule.isVimeoApiLoaded = false;
myModule.loaded = false;
myModule.controller;
myModule.loadYoutubeapi = function() {
/* Do your stuff. */
myModule.isYoutubeApiLoaded = true;
myModule.moduleHasLoaded();
}
myModule.loadVimeoApi = function() {
/* Do your stuff. */
myModule.isVimeoApiLoaded = true;
myModule.moduleHasLoaded();
}
myModule.moduleHasLoaded = function() {
if (!loaded && myModule.isYoutubeApiLoaded && myModule.isVimeoApiLoaded ) {
loaded = true;
myModule.buildGallery();
}
}
myModule.init = function (){
myModule.loadYoutubeapi();
myModule.loadVimeoApi();
}
The simple solution is to use a script loader like lazyload. It allows you to specify the scripts you want to load and run a callback when the are actually loaded, i.e.:
LazyLoad.js([
"http://www.youtube.com/apiplayer?enablejsapi=1&version=3",
"...vimeo script...",
"...other scripts..."
],
function() {
myModule.buildGallery();
}
);
The function will be called when all scripts are loaded. The caveat is that you get no error callback, i.e. if a script fails to load. There are also other script loaders besides lazyload.
More complicated solution, but better if you are working on a medium to large size client-side application: Refactor your modules to use require.js.

How to determine when all scripts finished running?

I am using Javascript and jQuery to render a page.
The page has to be loaded very quickly. Therefore I would like to render some basic info first and only after that send another request to get some additional info.
My question is when should I send that second request? How do I know when the basic info has completely finished rendering and no scripts are still running?
May be better would be use AMD approach? Besides requirejs there is similar lib that me liked.
This sounds like a job for yepnope
var asyncOperations = {
counter: -1, // -1 because the value should not already met the ending condition at initialization
finishedHandler: null;
startOp: function() {
if(counter == -1) counter = 0;
counter++;
},
endOp: function() {
counter++;
if(counter == 0 && finishedHandler) {
finishedHandler.inform();
}
}
function handler(data) {
doSomethingWithData();
asyncOperations.endOp();
}
function allOperationsFinished() {
alert("success");
}
function main() {
asyncOperations.finishedHandler = allOperationsFinished;
$.ajax(...whatever..., handler);
}
Set up a semaphore that you can check with a timer. Say you have 5 scripts running. Set a global variable finishedRunning=0;. In each script, just before returning (ie finishing) do finishedRunning+=1;. When finishedRunning reaches 5, you are good to go.

Loading external Javascript Sequentially

I am working on a javascript that sequentially loads a list of other external javascript.
The code I have so far:
function loadJavascript(url){
var js = document.createElement("script");
js.setAttribute("type", "text/javascript");
js.setAttribute("src", url);
if(typeof js!="undefined"){
document.getElementsByTagName("head")[0].appendChild(js)
}
}
loadJavascript("Jquery.js");
loadJavascript("second.js");
loadJavascript("third.js");
The problem I ran into is that sometimes the other js files loads before the Jquery file completes its loading. This gives me some errors.
Is it possible to make it so that the next JS file is only initiated when the previous file is finished loading.
Thanks in advance
Sure there is, but there's entire libraries written around doing this. Stop reinventing the wheel and use something that already works. Try out yepnope.js or if you're using Modernizr it's already available as Modernizr.load
loadJavascript("Jquery.js");
$(function(){
$.getScript('second.js', function(data, textStatus){
$.getScript('third.js', function(data, textStatus){
console.log("loaded");
});
});
}
Also, consider using the Google or Microsoft CDN for the jQuery, it will save you bandwidth and hopefully your visitors will already have it cached.
Actually, it's not necessary to load jquery within a js function. But if you insist, you can callback to make sure other js loaded after jquery.
Still, I recommend you load jquery just before </body> then use $.getScript to load other .js
You could do a check to see if jQuery is loaded, not the best way to do it, but if you really have to wait until jQuery is loaded before loading the other scripts, this is how I would do it, by checking for $ :
loadJavascript("Jquery.js");
T=0;
CheckIfLoaded();
function CheckIfLoaded() {
if (typeof $ == 'undefined') {
if (T <= 3000) {
alert("jQuery not loaded within 3 sec");
} else {
T=T+200;
setTimeout(CheckIfLoaded, 200);
} else {
loadJavascript("second.js");
loadJavascript("third.js");
}
}
In technical terms: Browsers have a funny way of deciding I which order to execute/eval dynamically loaded JS, so after suffering the same pain and checking a lot of posts, libraries, plugins, etc. I came up with this solution, self contained, small, no jquery needed, IE friendly, etc. The code is extensively commented:
lazyLoader = {
load: function (scripts) {
// The queue for the scripts to be loaded
lazyLoader.queue = scripts;
lazyLoader.pendingScripts = [];
// There will always be a script in the document, at least this very same script...
// ...this script will be used to identify available properties, thus assess correct way to proceed
var firstScript = document.scripts[0];
// We will loop thru the scripts on the queue
for (i = 0; i < lazyLoader.queue.length; ++i) {
// Evaluates if the async property is used by the browser
if ('async' in firstScript ) {
// Since src has to be defined after onreadystate change for IE, we organize all "element" steps together...
var element = document.createElement("script");
element.type = "text/javascript"
//... two more line of code than necessary but we add order and clarity
// Define async as false, thus the scripts order will be respected
element.async = false;
element.src = lazyLoader.queue[i];
document.head.appendChild(element);
}
// Somebody who hates developers invented IE, so we deal with it as follows:
// ... In IE<11 script objects (and other objects) have a property called readyState...
// ... check the script object has said property (readyState) ...
// ... if true, Bingo! We have and IE!
else if (firstScript.readyState) {
// How it works: IE will load the script even if not injected to the DOM...
// ... we create an event listener, we then inject the scripts in sequential order
// Create an script element
var element = document.createElement("script");
element.type = "text/javascript"
// Add the scripts from the queue to the pending list in order
lazyLoader.pendingScripts.push(element)
// Set an event listener for the script element
element.onreadystatechange = function() {
var pending;
// When the next script on the pending list has loaded proceed
if (lazyLoader.pendingScripts[0].readyState == "loaded" || lazyLoader.pendingScripts[0].readyState == "complete" ) {
// Remove the script we just loaded from the pending list
pending = lazyLoader.pendingScripts.shift()
// Clear the listener
element.onreadystatechange = null;
// Inject the script to the DOM, we don't use appendChild as it might break on IE
firstScript.parentNode.insertBefore(pending, firstScript);
}
}
// Once we have set the listener we set the script object's src
element.src = lazyLoader.queue[i];
}
}
}
}
Of course you can also use the minified version:
smallLoader={load:function(d){smallLoader.b=d;smallLoader.a=[];var b=document.scripts[0];for(i=0;i<smallLoader.b.length;++i)if("async"in b){var a=document.createElement("script");a.type="text/javascript";a.async=!1;a.src=smallLoader.b[i];document.head.appendChild(a)}else b.readyState&&(a=document.createElement("script"),a.type="text/javascript",smallLoader.a.push(a),a.onreadystatechange=function(){var c;if("loaded"==smallLoader.a[0].readyState||"complete"==smallLoader.a[0].readyState)c=smallLoader.a.shift(),
a.onreadystatechange=null,b.parentNode.insertBefore(c,b)},a.src=smallLoader.b[i])}};

How to make my function to be page specific or div id specific?

I am writing javascript to my web pages, but there is a number of functions and loops, that i think are running in all pages, so the first one is running and failing on the second page. Because of this, the javascript function on the second page is not running.
Can anyone give me an idea of how to create page-specific functions or check the availability of an id? I don't use any frameworks.
thanks in advance.
my javascript code is :
window.onload = function(){
var yellows = document.getElementById('magazine-brief').getElementsByTagName('h2');
var signUp = document.getElementById('signup-link');
function animeYellowBar(num){
setTimeout(function(){
yellows[num].style.left = "0";
if(num == yellows.length-1){
setTimeout(function(){
signUp.style.webkitTransform = "scale(1)";
},num * 250);
}
}, num * 500);
}
for (var i = 0; i < yellows.length; i++){
animeYellowBar(i);
}
alert("alert second page");
}
in this code, the alert message not working on second page. any idea?
If I understand you correctly, you have a javascript function, that you want to attach to an event from a specific div element in your page.
a) Include an event directly to you HTML page, something like this:
<div id="element" onclick="some_function();">Text is here</div>
b) Use a javascript function (add this code between <script> tag):
window.onload = function() {
document.getElementById("element").setAttribute("onclick", "some_function()")
}
The best way would be to only include those scripts on the pages which need them. Why waste time loading and parsing scripts you don't need?
If you must keep them on every page, put your functions in an if statement and check for something unique to the page that needs them (such as a form element or field ID).
Update
In response to your comment:
You have to code more defensively. You are attempting to make use of the magazine-brief and signup-link elements before you have made certain that they exist. Never trust that the proper element was returned - always check that it was before attempting to use that element.
I suggest checking your vars like so:
var yellows = document.getElementById('magazine-brief').getElementsByTagName('h2');
var signUp = document.getElementById('signup-link');
if (yellows != 'undefined' && signUp != undefined)
{
function animeYellowBar(num)
{
//...
}
}

Categories