I am currently trying to implement smoothState.js on my website. I have, however, run into a problem when transitioning between pages.
The problem is that the new content is 'unstyled' (more accurately put it is styled with the styles of the previous page). Since I have focused quite a lot on performance for this site, I would prefer if there was a way in which I could avoid combining the stylesheets.
I have thought a bit about how to tackle this problem, but cannot decide which of the solutions would have least performance impact. These are the possible solutions which I could think of.
1. Combine the stylesheets
Despite me being reluctant to do this, it might be the best way. After all, it is only a few extra bytes, right? Stylesheets (minified and gzipped) are quite small, after all.
2. Add a javascript function to the body
For smoothState, I am currently using the body as the container. I guess that I could thus, for example, add the following javascript directly after the body tag.
var cb = function() {
var l = document.createElement('link'); l.rel = 'stylesheet';
l.href = 'css/single.css';
var h = document.getElementsByTagName('head')[0]; h.parentNode.insertBefore(l, h);
};
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);
(I would have to modify the above since document.load isn't run when changing pages with smoothState. Sadly, I am quite terrible at vanilla JS. But I guess I can figure it out... Not a big problem :) )
3. Do some javascript magic in the onStart.render() of smoothstate()
Thirdly, and lastly, I realise that it would be plausible to run some javascript to get the stylesheet of the target page and append it to the new. My instinct tells me this will be less efficient performance wise than just combining the stylesheets...
In case it helps, I will include my smoothState function below.
'use strict';
var $body = $('html, body'),
content = $('#main').smoothState({
prefetch: true,
pageCacheSize: 4,
development: true,
onStart: {
duration: 250,
render: function (url, $container) {
// #3 would be implemented here.
content.toggleAnimationClass('is-exiting');
$body.animate({
scrollTop: 0
});
}
},
onProgress : {
duration: 0,
render: function (url, $container) {
$body.css('cursor', 'wait');
$body.find('a').css('cursor', 'wait');
}
},
onEnd: {
duration: 250, // Duration of our animation
render: function (url, $container, $content) {
$body.css('cursor', 'auto');
$body.find('a').css('cursor', 'auto');
$container.html($content);
}
}
}).data('smoothState');
})(jQuery);
Can anyone suggest which of the three methods provided would have the least impact on the page-load of my pages, or suggest another method of tackling the problem?
UPDATE 28/04/15
I have chosen to simply go with method number 1 and combined the stylesheets.
Can anyone suggest which of the three methods provided would have the
least impact on the page-load of my pages, or suggest another method
of tackling the problem?
You could also include new stylesheet references inside of the 'div#main' of the target page. When you inject the new html the browser will understand and execute any new <link rel="stylesheet" href="" /> being introduced.
I would personally still prefer to concatenate all of the CSS files, though.
Related
I'm working with a script that needs to delay (or have a setTimeout) before any animation loads or initializes, but can't seem to figure out where to put it.
As for delay, if I'm not mistaken, this is used mainly with jquery...So for example: $('id or class here').delay(2000); ...Correct?
As for the setTimeout, if I'm not mistaken, it would be with javascript correct? If so, wouldn't it look something similar to this: setTimeout(function () {function_name},2000); or a slightly different variation of that?
Regardless of these two approaches and trying to add it where I think it should go (using either variations mentioned above), for some reason it just doesn't work right. The console isn't really helping either to check for errors.
In a nutshell, I'm trying to set a delay of 2s (2000ms) before anything starts or initializes.
JS CODE (Where I believe the issue lies):
$(document).ready(function() {
// Additional code here...
// start
BG.init();
// Additional code here...
}
});
Where you have this:
$(document).ready(function() {
Put this:
$(document).ready(function() {
setTimeout(function() {
And then where you have this:
});
// wrapper for background animation functionality
var BG = {
Put this:
}, 2000);
});
// wrapper for background animation functionality
var BG = {
And then, if you don't want to incur the wrath of everyone in the world, indent the stuff inside that new function we just created by one more level. 'Cause indentation is the stuff of life.
There's a lot of 'useless' code for us to help you. Next time share only on a need-to-know bases :)
I've edited your $document.ready block to include the timeout, have a look-see:
$(document).ready(function() {
function initiationProcess() {
// setup logo image
BG.logo = new Image();
BG.logo.onload = function() {
BG.logo_loaded = true;
BG.showLogo();
}
// /../ more code /../
// wire ticker listener
Ticker.addListener(BG.tick);
// start
BG.init();
// /../ more code /../
}
setTimeout(initiationProcess, 2000);
});
Edit:
I'd also like to note that it's considered bad practise (not to mention that it might result in buggy code) to only partly use semicolons in your script file. There's points and counterpoints to using semicolons, but pick a standard and stick to it!
I have OpenCart application. Javascripts are loaded in settings.php inside path '/catalog/controller//settings.php with similar codes as:
$this->document->addScript('catalog/view/theme/<theme>/lib/lazy/jquery.lazy.1.6.min.js');
$this->journal2->minifier->addScript('catalog/view/theme/<theme>/lib/actual/jquery.actual.min.js', 'header');
$this->journal2->minifier->addScript('catalog/view/theme/<theme>/lib/hover-intent/jquery.hoverIntent.min.js', 'footer');
Here, 'theme' means theme name that is installed. I want to defer or async these javascript loading in OpenCart, how can I do it?
I know that addScript syntax has 1s parameter as file, second location, 3rd defer and 4th async where defer and async can be boolean.
I have tried statement as below to see defer false and async true:
$this->journal2->minifier->addScript('catalog/view/theme/<theme>/lib/hover-intent/jquery.hoverIntent.min.js', 'footer', false, true);
but I am not sure if this will work or not. Please suggest
Here is a script I've been using for quite some time, in the head element.
With this you get good control of your loading of files, and can start loading anything after all the DOM is loaded, just make sure the files is not required anywhere in the DOM upon load.
<head>
<title></title>
<link rel='stylesheet' type='text/css' href='css.css' />
<script type='text/javascript'>
var DomLoaded = {
done: false, onload: [],
loaded: function () {
if (DomLoaded.done) return;
DomLoaded.done = true;
if (document.removeEventListener) { document.removeEventListener('DOMContentLoaded', DomLoaded.loaded, false); }
for (i = 0; i < DomLoaded.onload.length; i++) DomLoaded.onload[i]();
},
load: function (fireThis) {
this.onload.push(fireThis);
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', DomLoaded.loaded, false);
} else {
/*IE<=8*/
if (/MSIE/i.test(navigator.userAgent) && !window.opera) {
(function () {
try { document.body.doScroll('up'); return DomLoaded.loaded(); } catch (e) { }
if (/loaded|complete/.test(document.readyState)) return DomLoaded.loaded();
if (!DomLoaded.done) setTimeout(arguments.callee, 10);
})();
}
}
/* fallback */
window.onload = DomLoaded.loaded;
}
};
DomLoaded.load(function () {
var d = document;
var h = d.getElementsByTagName('head')[0];
var s = d.createElement('script');
s.setAttribute('type', 'text/javascript');
s.setAttribute('async', true);
s.setAttribute('defer', true);
s.setAttribute('src', '/path/to/scripts.js');
h.appendChild(s);
});
</script>
</head>
And here is a good article which describes a few more ways to speed things up, and one of them is combining your js files into 1, or 2-3-4, based on which one needs which. The benefit is less http request.
http://exisweb.net/web-site-optimization-making-javascript-load-faster
And google is filled with articles
https://www.google.com/search?q=speed%20up%20loading%20javascript%20files&rct=j
For reducing page load times, you must do two mainly things
Reduce number of requests
Reduce package sizes
For reducing number of requests, you may merge all javascripts & css to one file. You should also applying lazy load images (This helps reduce package size too)
If you are running a VPS, you may try to install mod_pagespeed (developed by google) - It will help decrease a lot page load time.
I am not sure if you have used gtmetrix.com or http://www.webpagetest.org/
for reviewing your site speed yet.
For my experience, lazy load JavaScript will not help you much
When I click on my website sometimes you can visually see the images slowly loading into the place. I don't really like this and if possible would like to prevent it from happening.
From reading round it seems preloading images is the solution I'm looking for? Let me know if that's correct or if there is a better way.
On this forum I see lots of answers to preloading images and below is a code I think works but I want to change it slightly:
var preloadImages = [ img/1.jpg,img/2.jpg];
for(var i = 0 ; i < preloadImages.length; i++) {
new Image().src = preloadImages[i];
}
I think this code above works but it requires me to type in every image source into an array. On my website there are lots of images and I will probably continue to add more. So is there a way to push all the image sources into the array without actually typing them in. So as I add more images the preloading take care of itself.
If you wanted to go down the route you already have you could search the entire DOM for tags first and then add them to the array.
Perhaps something along these lines:
images = []
$(body).find('img').each ->
imgSrc = $(this).attr('src')
image.push imgSrc
i = 0
while i < preloadImages.length
(new Image).src = preloadImages[i]
i++
That will need jQuery and probably some tweaking. I can't imagine this being the best approach however as you still need to wait for the DOM, then jQuery to load and then run through the entire DOM with the function(s).
If the issue is more that the content is moving around you can (and probably should) prevent this by giving the tag itself a height and width if you know it in advance.
Additionally it's always worth optimizing your images (either by hand or on build/compile using something like Gulp.js) if you haven't already. It's incredible how much you can reduce load times by simply by using the optimal image (in terms of size and format).
You can call below function after page load
function preloadImages(){
$('img').each(function(){
new Image().src = this.src;
});
}
You can use this function to set multiple after-page-load functions
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = function(){
func();
};
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
};
}
}
then add your function like this
addLoadEvent(preloadImages);
But I don't think you can really pre-load images like this. To really pre-load images, you have to start loading those images as soon as your DOM starts to render on the client-side. And that means loading them in the head section. But if you do that, because you don't have your html just yet, you will not be able to run the above function, because it needs the DOM to be fully downloaded. You have to hardcode your image sources into javascript like you already did, and put your function as the first thing in the head section.
First I have to say that I am not a professional programmer but designer learning by doing. So I am afraid I need simple explanations if possible.
I am using Edge animate as parts of a website with the help of a particular script (by Andrew Trice, see here: http://www.tricedesigns.com/2012/07/12/using-adobe-edge-animations-as-components/ ). I also succeeded in storing only 1 Edge preload.js file in my libs folder. In it I introduced a variable at the end in order to be able to load Edge animations one after another. Code:
function setupAnimationView( template ) {
var $workPage = $("#workPage")
$workPage.empty();
window.AdobeEdge = undefined;
AdobeEdge = undefined;
var viewModel = { workChart: workChart };
var html = Mustache.to_html(template, viewModel)
$workPage.append( html );
//detect if edge is loaded yet
var edgeDetectionFunction = function() {
if ( AdobeEdge && AdobeEdge.compositions != undefined ) {
//put a delay here
var hasComposition = false;
if ( AdobeEdge.compositions ) {
//loop to see if the composition is actually loaded
for ( var key in AdobeEdge.compositionDefns ) {
hasComposition = true;
break;
}
}
if ( hasComposition ) {
setTimeout( function() {
$(window).trigger( "animationReady" ); }, 100 );
return;
}
}
else if ( AdobeEdge ) {
window.onDocLoaded();
}
setTimeout( edgeDetectionFunction, 100 );
}
edgeDetectionFunction();
}
Modified Adobe Edge preload.js:
...
requiresSVG=false;
doDelayLoad=false;
htFallbacks={
};
aLoader = [
{ load: "libs/jquery-1.10.2.min.js"},
{ load: "libs/jquery.easing.1.3.js"},
{ load: "libs/jquery.rotate.js"},
{ load: "libs/edge.1.0.0.min.js"},
{test: !hasJSON, yep:"libs/json2_min.js"},
{ load: "templates/Chart_" + workChart + "/Page_work_edge.js"},
{ load: "templates/Chart_" + workChart + "/Page_work_edgeActions.js"}];
loadResources(aLoader, doDelayLoad);
preContent={dom:[
]}
;//simpleContent
dlContent={dom: [
]}
;//simpleContent
//updated paths for loader
//added this so it can be invoked later
window.onDocLoaded = onDocLoaded;
})( "animation_content");
So far thanks to Andrew everything works very fine except that I saw in my developer tool (Safari) that my code leads to loading the basic js files like jquery-1.10.2.min.js over and over again each time a new animation starts to run and this files are summing up to an endless number… which I guess isn’t a good thing.
I even understand why (at least I believed) and so I deleted the respective lines in the aLoader object at the end of preload. (of course they are loaded within the script tag in my index page)
aLoader = [
{ load: "libs/jquery-1.10.2.min.js"}, // deleted this one
{ load: "libs/jquery.easing.1.3.js"}, // deleted this one
{ load: "libs/jquery.rotate.js"}, // deleted this one
{ load: "libs/edge.1.0.0.min.js"}, // deleted this one
{test: !hasJSON, yep:"libs/json2_min.js"},
{ load: "templates/Chart_" + workChart + "/Page_work_edge.js"},
{ load: "templates/Chart_" + workChart + "/Page_work_edgeActions.js"}];
because I can’t see why it would be necessary to load them more than once. Yet, after doing so only the first animation runs, the second does not any longer. But why? I checked in the browser and see that jquery-1.10.2.min.js is in the page so why can’t (or seems so) the Edge files use it any longer? The same for the other ones (rotate etc.).
I also tried to suppress these two lines from the function above:
window.AdobeEdge = undefined;
AdobeEdge = undefined;
without any result though.
Where is the trick to avoid reloading those basic needed .js files? Any ideas? Thank you so much for advice
Garavani
#Jamie
EDIT:
So ,hello Jamie! I was so curious about your solution that I dropped everything else and tried it. Sadly it does not work in the situation I tried to explain as exactly as possible in the lines above this edit.
To avoid any misunderstandings here my changed code following your instructions (very good explanation by the way!):
edgePreload.js (in my version it has no filename addition and lies in my „lib“ folder that is accessed by each new animation after the method by Andrew - see above!) :
window.AdobeEdge = window.AdobeEdge || {};
window.AdobeEdge.$_ = $;
// Include yepnope
if(!AdobeEdge.yepnope) {…
Then further on:
$(function() { // your insert opening function
(function(compId){
var htFallbacks; …
…
window.onDocLoaded = onDocLoaded;
})( "animation_content");
}); // your insert closure
My index.html contains the following scripts (among others):
<script src="libs/jquery-1.11.0.min.js"></script>
<script src="libs/jquery.easing.1.3.js"></script>
<script src="libs/jquery.rotate.js"></script>
<script src="libs/edge.1.0.0.min.js"></script>
<script src="libs/mustache.js"></script>
<script src="libs/mustacheHelper.js"></script>
which I like to host on my server. (avoiding all kinds of trouble coming up with versions updates not under my control)
Now I dared and hoped to be able to cancel that stuff from my aLoader arrow as follows:
aLoader = [
// { load: "libs/jquery-1.11.0.min.js"},
// { load: "libs/jquery.easing.1.3.js"},
// { load: "libs/jquery.rotate.js"},
// { load: "libs/edge.1.0.0.min.js"},
{test: !hasJSON, yep:"libs/json2_min.js"},
{ load: "templates/Chart_" + workChart + "/Page_work_edge.js"},
{ load: "templates/Chart_" + workChart + "/Page_work_edgeActions.js"}];
but id t doesn’t work (throwing all kinds of errors obviously based on lacking edge codes).
When I replace all in the aLoader again at least it works without showing errors concerning the new inserted lines. But I have no result. Yet, would be soooooooooo cool!!!
Did I miss something? Did you really check thoroughly what I did in my initial lines above?
I am curious to hear your ideas!
Thank you so far for your interest and willing to share your knowledge of this complicated edge material (not much discussed - for good reasons I guess – here in stack overflow).
I've been having the exact issue and you've probably solved it by now, but hopefully it'll help someone else.
1) Make sure you get your files by choosing 'HTML' in your publish settings, and publishing to a clean folder.
2) Use a prettifier tool (e.g. http://jsbeautifier.org/) to make the [filename]_edgePreload.js file readable.
3) Add this line window.AdobeEdge.$_ = $; just under the top line window.AdobeEdge = window.AdobeEdge || {};
4) Find this line (function(compId) { (which is the start of the load method) and wrap that whole method (everything between here and the bottom of the page) with $(function() { and });
5) In the aLoader array, remove the first two entries that specify how to load edge and jquery.
6) Add the edge library (currently https://animate.adobe.com/runtime/4.0.1/edge.4.0.1.min.js) to the head of your site, along with jQuery.
Don't feel too bad that this was confusing, the Adobe Edge output code has been written by a cabbage with legs. Everyone struggles with it :)
The title of this post reads as webdev-hipster as a skinny flannel scarf at an alleycat race. Sorry.
I'm not great with script runtime optimization, so I'm wondering just how bad computationally the following function call is going to be. I know it wouldn't be practical for a big site, but where I wan to use it, the jQuery call is going to return no more than a half dozen objects, so the volume isn't high.
Modernizr.load({
test: Modernizr.borderradius && Modernizr.boxshadow,
nope: "_/js/polyfills/pie.js",
complete: function(){
if(window.PIE){
$('*').css('box-shadow').each(function(){ PIE.attach(this); });
$('*').css('border-radius').each(function(){ PIE.attach(this); });
}
}
});
Thanks everyone.
Try this.
Modernizr.load({
test: Modernizr.borderradius && Modernizr.boxshadow,
nope: "_/js/polyfills/pie.js",
complete: function(){
if(window.PIE){
$('*').each(function(){
var $this = $(this);
//check if box-shadow or border-radius is applied
if($this.css('box-shadow') || $this.css('border-radius')){
PIE.attach(this);
}
});
}
}
});
...the jQuery call is going to return no more than a half dozen objects...
So half a dozen is six. Four of those will be html, head, script, and body. :-) You only have two other elements on the page?
Seriously, if the number is very low, it doesn't matter. You'd want to limit the $() call to only the elements that really need it, though, rather than $("*") which is a big hammer.
If you really need to run through the whole document, use a simple recursive-descent function:
function applyPie(element) {
var node;
for (node = element.firstChild; node; node = node.nextSibling) {
if (node.nodeType === 1) { // 1 = element
node.style.boxShadow = /* ...?... there's nothing in your jQuery call */;
node.style.borderRadius = /* ...?... there's nothing in your jQuery call */;
PIE.attach(node);
applyPie(node);
}
}
}
applyPie(document.documentElement);
That calls PIE.attach on every element except the documentElement. You might use nodeName (or tagName) so you don't attach PIE to html, head, style, and such. Using a simple recursive-descent function avoids creating large flat arrays in memory, which is what $("*") does.