How to use window onload in a single page application? - javascript

I'm using node.js in order to build a single page application.
At the top of the homepage I used a 100vh css style to show some color in full screen, and then after page loads, I created a transition to load an image.
I used this code:
JS
$('body').addClass('is-loading');
$(window).on('load', function() {
$('body').removeClass('is-loading');
});
However, single page application doesn´t need to load again after it loads. So, my question is how can I achieve this transition using node.js? Is there an alternative?

Window onload would works fine in a static web page. In a single page application you can use setTimeout (as one possible solution) to removeClass.
For what I understand you want transition works any time in your app:
This code works fine (is already tested):
if ( ! $('body').is('.is-loading') ) {
$('body').addClass('is-loading');
}
setTimeout(function () {
$('body').removeClass('is-loading');
}, 1000)
I hope it works in your code. Would be great to see some example of your css code. For now, you can achieve transition with the example written above.
Change time parameter according to your needs.

You can use localStorage.
Using a native js approach
localStorage.setItem('myToggle', isLoaded);
and then you retrieve from the same local storage using getItem and then specifying the name of the created localStorage item.
var isItAlreadyLoaded = localStorage.getItem('myToggle');
So
window.onload = function(){
var isItAlreadyLoaded = localStorage.getItem('myToggle');
if(isItAlreadyLoaded == 'true'){
console.log('already loaded');
}
else{
//load image here
//.....
var isLoaded = 'true';
localStorage.setItem('myToggle', isLoaded);
console.log('saved!');
}
}
This works like magic. localStorage is some sort of browser mini-storage, each storage variable can hold upto 5~10mb depending on the browser. It works well in most modern browsers.
All I did was put a boolean variable to check if the image was already loaded, or just check for the existence of the localStorage.

Related

Add class to dynamically generated HTML

I'm working to use custom checkbox styles with a checkbox which is dynamically generated by javascript for the Google Identity Toolkit. For example, we add this div:
<div id="gitkitWidgetDiv"></div>
And the Google Identity Toolkit script generates new html for that div.
I need to add a class to the HTML which is added by the javascript without any action by the user and I'm struggling to make it work. For example, here is my code:
$("#gitkitWidgetDiv").on('ready', ".gitkit-sign-in-options label", function() {
$(this).addClass('checkbox');
});
I've tried switching 'ready' for a few other options and also using the livequery plugin, but nothing is working for me. It works if I use an active event like 'click,' but I can't figure out how to do this when the page loads. Could someone please help? Thanks!
Modern browsers (including IE11) support mutation obervers. You can use one to monitor the parent node of the div that will be added. When the div has been added, just add the class.
Here's something I made which comes in handy in annoying cases like this where it's difficult to tell when the element you need has finished loading in: https://gist.github.com/DanWebb/8b688b31492632b38aea
so after including the function it'd be something like:
var interval = 500,
stopTime = 5000,
loaded = false;
setIntervalTimeout(function() {
if($('.dynanicElementClass').length && !loaded) {
$('.dynanicElementClass').addClass('checkbox');
loaded = true;
}
}, interval, stopTime);
It's not perfect and I'm sure there are better solutions out there but in most cases like this it does the job.

Call function anytime a link is moused over on an arbitrary page?

I apologize for the possibly naive nature of this question but I am not a web developer by day.
Is it possible to write a script such that, for any arbitrary web page, a function that I have written will be called if a URL is moused over? I was initially thinking that I could use document.links to assemble an array of all of the hrefs in a document and add an onmouseover event attribute to each of them but, unless I'm mistaken, that would overwrite any existing onmouseover attributes already present in the page. Not ideal.
I'm not sure if by arbitrary web page you mean any pages on any domains or any pages of your own domain, but for the latter you could put something like the following in your pages:
$(function () {
$(document).on('mouseenter', 'a', function () {
console.log(this, 'hovered');
});
});
If you mean any page your browse to on the net, then you will have to write a browser extension for the browser your are using. For Chrome have a look at this.
You could try getting everything with the a tag and inject an onmouseover.
window.onload = function(){
for(m=0;m<document.getElementsByTagName('a');m++){
if(document.getElementsByTagName('a')[m].className == 'someclass'){
document.getElementsByTagName('a')[m].onmouseover = function(){
Your Code
}
}
}
}

Trying to load an API and a JS file dynamically

I am trying to load Skyscanner API dynamically but it doesn't seem to work. I tried every possible way I could think of and all it happens the content disappears.
I tried console.log which gives no results; I tried elements from chrome's developers tools and while all the content's css remains the same, still the content disappears (I thought it could be adding display:none on the html/body sort of). I tried all Google's asynch tricks, yet again blank page. I tried all js plugins for async loading with still the same results.
Skyscanner's API documentation is poor and while they offer a callback it doesn't work the way google's API's callback do.
Example: http://jsfiddle.net/7TWYC/
Example with loading API in head section: http://jsfiddle.net/s2HkR/
So how can I load the api on button click or async? Without the file being in the HEAD section. If there is a way to prevent the document.write to make the page blank or any other way. I wouldn't mind using plain js, jQuery or PHP.
EDIT:
I've set a bounty to 250 ontop of the 50 I had previously.
Orlando Leite answered a really close idea on how to make this asynch api load although some features doesn't work such as selecting dates and I am not able to set styling.
I am looking for an answer of which I will be able to use all the features so that it works as it would work if it was loading on load.
Here is the updated fiddle by Orlando: http://jsfiddle.net/cxysA/12/
-
EDIT 2 ON Gijs ANSWER:
Gijs mentioned two links onto overwriting document.write. That sounds an awesome idea but I think it is not possible to accomplish what I am trying.
I used John's Resig way to prevent document.write of which can be found here: http://ejohn.org/blog/xhtml-documentwrite-and-adsense/
When I used this method, I load the API successfuly but the snippets.js file is not loading at all.
Fiddle: http://jsfiddle.net/9HX7N/
I belive what you want is it:
function loadSkyscanner()
{
function loaded()
{
t.skyscanner.load('snippets', '1', {'nocss' : true});
var snippet = new t.skyscanner.snippets.SearchPanelControl();
snippet.setCurrency('GBP');
snippet.setDeparture('uk');
snippet.draw(document.getElementById('snippet_searchpanel'));
}
var t = document.getElementById('sky_loader').contentWindow;
var head = t.document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.onreadystatechange= function() {
if(this.readyState == 'complete') loaded();
}
script.onload= loaded;
script.src= 'http://api.skyscanner.net/api.ashx?key=PUT_HERE_YOUR_SKYSCANNER_API_KEY';
head.appendChild(script);
}
$("button").click(function(e)
{
loadSkyscanner();
});
It's load skyscanner in iframe#sky_loader, after call loaded function to create the SearchPanelControl. But in the end, snippet draws in the main document. It's really a bizarre workaround, but it works.
The only restriction is, you need a iframe. But you can hide it using display:none.
A working example
EDIT
Sorry guy, I didn't see it. Now we can see how awful is skyscanner API. It puts two divs to make the autocomplete, but not relative to the element you call to draw, but the document.
When a script is loaded in a iframe, document is the iframe document.
There is a solution, but I don't recommend, is really a workaround:
function loadSkyscanner()
{
var t;
this.skyscanner;
var iframe = $("<iframe id=\"sky_loader\" src=\"http://fiddle.jshell.net/orlleite/2TqDu/6/show/\"></iframe>");
function realWorkaround()
{
var tbody = t.document.getElementsByTagName("body")[0];
var body = document.getElementsByTagName("body")[0];
while( tbody.children.length != 0 )
{
var temp = tbody.children[0];
tbody.removeChild( temp );
body.appendChild( temp );
}
}
function snippetLoaded()
{
skyscanner = t.skyscanner;
var snippet = new skyscanner.snippets.SearchPanelControl();
snippet.setCurrency('GBP');
snippet.setDeparture('uk');
snippet.draw(document.getElementById('snippet_searchpanel'));
setTimeout( realWorkaround, 2000 );
}
var loaded = function()
{
console.log( "loaded" );
t = document.getElementById('sky_loader').contentWindow;
t.onLoadSnippets( snippetLoaded );
}
$("body").append(iframe);
iframe.load(loaded);
}
$("button").click(function(e)
{
loadSkyscanner();
});
Load a iframe with another html who loads and callback when the snippet is loaded. After loaded create the snippet where you want and after set a timeout because we can't know when the SearchPanelControl is loaded. This realWorkaround move the autocomplete divs to the main document.
You can see a work example here
The iframe loaded is this
EDIT
Fixed the bug you found and updated the link.
the for loop has gone and added a while, works better now.
while( tbody.children.length != 0 )
{
var temp = tbody.children[0];
tbody.removeChild( temp );
body.appendChild( temp );
}
For problematic cases like this, you can just overwrite document.write. Hacky as hell, but it works and you get to decide where all the content goes. See eg. this blogpost by John Resig. This ignores IE, but with a bit of work the trick works in IE as well, see eg. this blogpost.
So, I'd suggest overwriting document.write with your own function, batch up the output where necessary, and put it where you like (eg. in a div at the bottom of your <body>'). That should prevent the script from nuking your page's content.
Edit: OK, so I had/took some time to look into this script. For future reference, use something like http://jsbeautifier.org/ to investigate third-party scripts. Much easier to read that way. Fortunately, there is barely any obfuscation/minification at all, and so you have a supplement for their API documentation (which I was unable to find, by the way -- I only found 'code wizards', which I had no interest in).
Here's an almost-working example: http://jsfiddle.net/a8q2s/1/
Here's the steps I took:
override document.write. This needs to happen before you load the initial script. Your replacement function should append their string of code into the DOM. Don't call the old document.write, that'll just get you errors and won't do what you want anyway. In this case you're lucky because all the content is in a single document.write call (check the source of the initial script). If this weren't the case, you'd have to batch everything up until the HTML they'd given you was valid and/or you were sure there was nothing else coming.
load the initial script on the button click with jQuery's $.getScript or equivalent. Pass a callback function (I used a named function reference for clarity, but you can inline it if you prefer).
Tell Skyscanner to load the module.
Edit #2: Hah, they have an API (skyscanner.loadAndWait) for getting a callback once their script has loaded. Using that works:
http://jsfiddle.net/a8q2s/3/
(note: this still seems to use a timeout loop internally)
In the skyrunner.js file they are using document.write to make the page blank on load call back... So here are some consequences in your scenario..
This is making page blank when you click on button.
So, it removes everything from page even 'jQuery.js' that is why call back is not working.. i.e main function is cannot be invoked as this is written using jQuery.
And you have missed a target 'div' tag with id = map(according to the code). Actually this is the target where map loads.
Another thing i have observed is maps is not actually a div in current context, that is maps api to load.
Here you must go with the Old school approach, That is.. You should include your skyrunner.js file at the top of the head content.
So try downloading that file and include in head tag.
Thanks

Best way to define a class with a cookie

I have a switcher that is changing backgrounds and setting a cookie using the jquery cookie script. It looks something like:
$(document).ready(function(){
var colors = $.cookie('colors');
$(".box_2f2").click(function(){
$('body').removeClass('c000 cfff c2f2');
$('body').addClass('c2f2');
$.cookie('colors','middleGrey');
return false;
});
$(".box_fff").click(function(){
$('body').removeClass('c000 cfff c2f2');
$('body').addClass('cfff');
$.cookie('colors','white');
return false;
});
$(".box_000").click(function(){
$('body').removeClass('c000 cfff c2f2');
$('body').addClass('c000');
$.cookie('colors','black');
return false;
});
if(colors == 'middleGrey')
{
$('body').addClass('c2f2');
}
if (colors == 'white')
{
$('body').addClass('cfff');
}
if(colors == 'black')
{
$('body').addClass('c000');
}
});
the problem with this is because the class isnt set until that piece of javascript is loaded or the content is cached, the page will blink the defualt white background before showing the cookied style. What is a better way to code this so the class is applied earlier in the dom and does not have to wait for all of the scripts?
By using jQuery, you're dependent on the scripts to be either loaded from the cache or the web server. Unfortunately you'll have that bit of a flash while everything gets loaded.
ALTERNATIVELY, however, you could have something like the following applied just after opening the BODY tag of your page using NORMAL JavaScript (without being dependent on a library or framework):
<script type="text/javascript">
document.body.style["display"] = "none";
</script>
...then just set the "display" CSS property of the body back to "block" or whatever your preference is at the very end of your jQuery script there.
This is the common flash-of-unstyled-markup problem.
You can frame the entire page in a div, mark it "display:none" or "visibility:collapse" at first, then display it at the end of your script. The page will be just the default browser background until your script completes, which may take some time.
Consider having a "loading" graphic or splash screen before your script runs (if it takes more than 100-200ms), so that the user will know that something is happening and should wait a bit.

Explicitly setting load order

I have a slight problem.
I need to show some safety information in a JS drawer that the page loads with open. The images creating the drawer are kind of hefty and as such the safety text in the drawer render prior to the background, creating a really ugly loading experiance. Is there a easy way I can say
DO NOT LOAD safetyText UNTIL safetyBG is loaded ?
We're not using any libraries (i.e. jQuery) so a JS solution would have to work as a script by itself.
If I understand you correctly, you only want to display safetyText once safetyBG has fully loaded. To achieve that, hide safetyText, then check on document.onload if safetyBG has loaded. If so, show text, if not, attach function to show text to safetyBG.onload:
function showSafetyText() {
//show text
}
window.onload = function() {
var safetyBG = document.getElementById("safetyBG"); // tweak as necessary
//have to check to see if img loaded, if you would simply assign showSafetyText
//to img.onload, it would never run if the img was fully loaded already
if (!safetyBG.complete) {
safetyBG.onload = showSafetyText;
} else {
showSafetyText();
}
}
You mentioned images (rather than a single image) in your post. If that's the case you'll want to write a function that loops through the images to check to see if they've all loaded (at which time you can load the text); then attach this function to each of the images' onload events (unless all images are loaded already in which case you can simply show the text).

Categories