Structure javascript and delay execution when dynamically loading scripts - javascript

I'm loading my javascript files using Modernizr.load(). Now lets say each page needs to call their own init method after everything has finished loading. But I put the loading in a template master, which is unaware of the child pages. How can the pages know when the dependencies have finished loading?
script
Page1Stuff= {
init: function(){ alert('page 1 executed'); }
}
Page2Stuff= {
init: function(){ alert('page 2 executed'); }
}
site master template
//Modernizr loads the script.
Modernizr.load([{
load: 'jquery.js',
},
{
load: 'script.js'
}]);
page1
$(function() {
Page1Stuff.init();
});
page2
$(function() {
Page2Stuff.init();
});
I think I see 2 problems here. If modernizr is still loading jquery, $ may not be defined. Also, Page2Stuff or Page1Stuff may not be defined.

In your template: Use the modernizer "complete" call back to emit a custom event to signal the loading is complete, also set a global variable saying that the scripts have been loaded.
In your page scripts: first check the global variable to see if all scripts have been loaded, if not, register a handler to listen for the custom event you emit when the loading completes.
In your template code:
// A simple object that calls its listeners when modernizer script loading completes.
global_scriptLoadingMonitor = (function() {
var listeners = [];
var isComplete = false;
return {
"addListener": function (listener) {
listeners[listeners.length] = listener;
},
"complete": function () {
isComplete = true;
for (var i = listeners.length - 1; i >= 0; i--) {
listeners[i]();
}
},
"isComplete": isComplete
}
})();
Modernizr.load([{
load: 'jquery.js',
},
{
load: 'script.js'
complete: function () {
{
global_scriptLoadingMonitor.complete();
}
}]);
Now in your Page1 do this:
if (global_scriptLoadingMonitor.isComplete) {
$(function() {
Page1Stuff.init();
});
} else {
global_scriptLoadingMonitor.addListener(function() {
$(function() {
Page1Stuff.init();
});
});
}
And similarly for page2.

Related

$(document).ready(function() with Meteor JS templates

I have a template I've deeply integrated with Meteor. It uses a scripts.js file that runs a bunch of commands after $(document).ready and $(window).load.
I put scripts.js inside of client/compatibility, and it works only if I do a hard refresh of the template. Otherwise, the template doesn't render and the functions don't execute.
Specifically, this code:
// Append .background-image-holder <img>'s as CSS backgrounds
$('.background-image-holder').each(function() {
var imgSrc = $(this).children('img').attr('src');
$(this).css('background', 'url("' + imgSrc + '")');
$(this).children('img').hide();
$(this).css('background-position', 'initial');
});
Much appreciated if you know how I should take it from here.
If you want a function to fire off when the DOM is loaded and all images, use this: (be forewarned, this is in ES6)
let imgCount;
let imgTally = 0;
Template.myTemplate.onRendered(function () {
this.autorun(() => {
if (this.subscriptionsReady()) {
Tracker.afterFlush(() => {
imgCount = this.$('img').length;
});
}
});
});
Template.myTemplate.events({
'load img': function (event, template) {
if (++imgTally >= imgCount) {
(template.view.template.imagesLoaded.bind(template))();
}
}
});
Then you just need to declare imagesLoaded which will get fired off when all images are done.
Template.myTemplate.imagesLoaded = function () {
// do something
};
Place this stuff in the onRendered function of the template.
Template.<name>.onRendered(function(){
//do your jquery business here!
});
For more info check the docs

Run javascript based on url

okay so I need this script
$(document).ready(function() {
$('a[id="twittersocial"]').click(function() {
$(".twittersocial").slideToggle("slow", function() {
$(".twittersocial").load('social/twitter.php?k=<?php echo"$website"; ?>', function() {});
});
});
});
to run when someone goes to the direct url
www.website.com#twittersocial
and when of course when somene click the link
twitter social
How can I achieve this ?
When the page is loaded, check whether the hash is set and run your code. Put the code in a function so you can call it from both the event handler and at startup.
$(function() {
function loadTwitterSocial() {
$(".twittersocial").slideToggle("slow", function() {
$(".twittersocial").load('social/twitter.php?k=<?php echo"$website"; ?>', function() {});
});
}
if (location.hash == "#twittersocial") {
loadTwitterSocial();
}
$('a#twittersocial').click(loadTwitterSocial);
}

PJAX/DJAX- rerun scripts after pjax load

I'm using RequireJS to structure my JS and DJAX (basically PJAX) as a way to dynamically load content without a full page reload.
The issue I have is that after DJAX has finished loading the content I need to rerun my scripts (e.g. a script that adds multiple images into a carousel) so that the new page renders correctly. I thought the best way to do this would be to simply rerun the function I'm running on $(document).ready(); but I'm getting this output in the console:
Uncaught TypeError: Cannot read property 'djaxLoad' of undefined
which is referring to this line in load-posts.js
bootstrap.init();
I'm guessing I'm writing something incorrectly.
Here is bootstrap.js which is my main file which fires on document.ready and initialises all my modules
require(['base/responsive', 'base/main-menu', 'base/social-share', 'base/randomise-colours', 'base/infinite-scroll', 'base/load-posts', 'base/image-fade', 'base/carousel', 'base/misc'],
function(responsive, main_menu, social_share, randomise_colours, infinite_scroll, load_posts, image_fade, carousel, misc) {
var $ = jQuery;
function djaxLoad() {
//If page has JS (detected with Modernizr) then hide content until the
//doc is ready to avoid flash of unstyled content
$('#djax-container').css('display', 'block');
main_menu.init();
social_share.init();
randomise_colours.init();
load_posts.init();
image_fade.init();
infinite_scroll.init();
carousel.init();
misc.init();
responsive.init();
}
$(document).ready(djaxLoad);
}
);
And this is load-posts.js which handles DJAX for me. DJAX works, I just need to get the scripts to fire again.
define(['djax', 'base/bootstrap'], function(djax, bootstrap) {
var $ = jQuery,
$body = $('body');
return {
init: function() {
if($body.length >= 1) {
this.setUp();
} else {
return false;
}
},
setUp: function() {
this.pjaxLinks();
},
pjaxLinks: function() {
//Initialise DJAX, but stop it from running on pagination links (URL is /page...)
$('body').djax('.updateable', ['page']);
//When clicking a djax link
$(window).bind('djaxClick', $.proxy(function() {
this.addLoader();
},this));
//When the new content has loaded in
$(window).bind('djaxLoad', $.proxy(function() {
this.removeLoader();
},this));
},
addLoader: function() {
$body.addClass('loader-visible');
},
removeLoader: function() {
$body.removeClass('menu-visible');
$('html, body').scrollTop(0);
function removeLoaderDelay() {
$body.removeClass('loader-visible');
bootstrap.djaxLoad();
}
setTimeout(removeLoaderDelay, 1000);
}
};
});
The djaxClick function runs on a link click, and the djaxLoad function runs after the content has been loaded in. I'm adding a loader overlay to the page, then removing it after the content has loaded in.
A bit late but for future reference: In the documentation at https://github.com/beezee/djax you'll find the djaxLoad-event. This is specifically made for your usecase. You can use it as follows:
$(window).bind('djaxLoad', function(e, data) {
// your scripts to run on ajax-calls
});
The site further explains: "the data object passed with the event contains the requested url, the page title for the requested page, and the contents of the requested page as a string".
$(window).bind('djaxLoad', function(e, data) {
var responseObj = $('<div>'+data.response+'</div>');
//do stuff here
});

Stop jquery function from automatically being called

I'm sure this is very simple, but I can't seem to find the answer.
I have a RoR app, and in my application.js file I want to call a function from within a function.
application.js:
jQuery(function_1($) {
$("#select_box").change(function() { ....
....
function_2 ();
return false;
});
jQuery(function_2 () {
...
return false;
);
function 1 is triggered when a select box is changed and works correctly. The issue is that function 2 is executed as soon as a new page is loaded. I only want function 2 to be called from within function 1.
How can I do that?
The problem is that when you put code inside of a block like this:
jQuery(function() {
$("#select_box").change(function() {
function_2();
return false;
});
});
The code is automatically executed. This is equivalent to
$(function() {
});
or
$(document).ready(function() {
});
Which should give you an idea of why function_2 is being invoked on page load. To remedy this, just define the function like this:
jQuery(function() {
var function_2 = function() {
return false;
};
$("#select_box").change(function() {
function_2();
return false;
});
});
See jQuery docs: http://learn.jquery.com/using-jquery-core/document-ready/
If you're using the asset pipeline, you shouldn't have javascript functions in application.js at all, it should just be a manifest. So, assuming you've disabled the asset pipeline, I think you just need to change how you define function_2. Try this:
var function_2 = function () {
...
return false;
};
$("#select_box").change(function() {
....
function_2 ();
return false;
});
function function_2 () {
...
return false;
}
Your question says:
function 1 is triggered when a select box is changed and works correctly. The issue is that function 2 is executed as soon as a new page is loaded. I only want function 2 to be called from within function 1.
$(document).on("change","#select_box",function(e) {
// used on function to incorporate for turbolinks
// your code
// to trigger your function1 when select box is changed
function1 ();
e.preventDefault();
});
function function1(){
//your code
// to trigger your function2 inside function1
function2();
}
function function2(){
//your code
}

Organzing javascript / jQuery for multiple web pages

To keep organized, I'd like to keep all the javascript for my site in a single file:
scripts.js
However, some of my scripts are only used on on some pages, other scripts are only used on other pages.
In my document-ready function it looks like this:
function home_page() {
// image rotator with "global" variables I only need on the home page
}
$('#form')... // jQuery form validation on another page
The problem with this, is that I am getting javascript to execute on pages it's not even needed. I know there is a better way to organize this but I'm not sure where to start...
One thing you could do would be to use classes on the <html> or <body> tag to establish the type of each page. The JavaScript code could then use fairly cheap .is() tests before deciding to apply groups of behaviors.
if ($('body').is('.catalog-page')) {
// ... apply behaviors needed only by "catalog" pages ...
}
Even in IE6 and 7, making even a few dozen tests like that won't cause performance problems.
I usually do something like this, or some variation (a little pseudo code below) :
var site = {
home: {
init: function() {
var self=this; //for some reference later, used quite often
$('somebutton').on('click', do_some_other_function);
var externalFile=self.myAjax('http://google.com');
},
myAjax: function(url) {
return $.getJSON(url);
}
},
about: {
init: function() {
var self=this;
$('aboutElement').fadeIn(300, function() {
self.popup('This is all about me!');
});
},
popup: function(msg) {
alert(msg);
}
}
};
$(function() {
switch($('body').attr('class')) {
case 'home':
site.home.init();
break;
case 'about':
site.about.init();
break;
default:
site.error.init(); //or just home etc. depends on the site
}
});
I ususally have an init() function that goes something like this:
function init() {
if($('#someElement').length>1) {
runSomeInitFunction()
}
... more of the same for other elements ...
}
Basically just check to see if the element exists on the page, if it does, run its own initialization function, if not, skip it.
The whole JS codes is cached by the browser after the first page load anyway, so there's no point in fragmenting your JS file down into page-specific pieces. That just makes it a maintenance nightmare.
You could use for each page object literals to get different scopes.
​var home = {
other: function() {
},
init: function() {
}
};
var about = {
sendButton: function(e) {
},
other: function() {
},
init: function() {
}
}
var pagesToLoad = [home, about];
pagesToLoad.foreach(function(page) {
page.init();
});​

Categories