First of, a quick word about my setup.
I have a global JS file common.js, where I store all commonly used functions. This file is bundled and set to load along with _Layout.cshtml view file.
Along with that, I have a view file, which generates an html object like following:
<a class="printreport" href="#" data-reporttype="8" data-certid="1111">Print</a>
Mentioned view file loads javascript source file through extended method
#section scripts {
#Html.LoadVersionedJavascriptFile("~/location/sourcefile.js")
}
In order to optimize my code, I decided to write a snippet function, which will make every html object with "printreport" class run it
Inside source file:
$(function(){
//other stuff
//Every html object that has printreport as a class will run this function
$(document.body).on('click', '.printreport', function () {
//get reporttype data from object
var reportType = $(this).data('reporttype');
var link = $("#RootDirectory").val();
switch (reportType) {
//other cases
case 8: //Certification
var certId = $(this).data('certid'); //Get certid data from object
link += '/Reports/ReportPopUp.aspx?report=' + reportType + '&CertId=' + certId;
break;
}
//code
});
});
Inside the source file, it responds and works as intended. However, if I try moving that snippet from the source.js into a global common.js (I confirmed that file is loaded during execution), it simply does not respond, and clicking on link does nothing:
Inside common.js file:
//declaration of global variables
$(document).ready(function () {
}
$(document.body).on('click', '.printreport', function () {
//code
}
Structure of common.js file is as above, it is not encapsulated into anything.
My question is: Am I loading document.body part improperly? What could be the possible cause of such unresponsiveness?
You should move your click handler inside $(document).ready:
$(document).ready(function () {
$(document.body).on('click', '.printreport', function () {
//code
}
}
$(document).ready is an event which fires up when document is ready and since your html is interpreted from top to bottom, your elements could not be present when your jQuery code runs ($(document.body)).
Related
I am using Meteor JS.
I have a JavaScript function defined in file A which I want to reuse by calling from file B. Example:
File A:
function Storeclass(){}
Storeclass.validate=function(){...}
From A JavaScript I try to call StoreClass.validateBasic() it works but the same call doesn't work from B. Also I tried in B doing var storeClassObj=new StoreClass(); and storeClassObj.validate(). I get error ReferenceError: StoreClass is not defined.
Read this doc about namespacing in Meteor.
The relevant portion is this:
// File Scope. This variable will be visible only inside this
// one file. Other files in this app or package won't see it.
var alicePerson = {name: "alice"};
// Package Scope. This variable is visible to every file inside
// of this package or app. The difference is that 'var' is
// omitted.
bobPerson = {name: "bob"};
However, later on in the same doc, it says this:
When declaring functions, keep in mind that function x () {} is just shorthand for var x = function x () {} in JavaScript.
This suggests that the function you have written is private to the file A and cannot be accessed from file B, even if load order is correct!
Because your function in file B might invoke before File A is ready so you have to make sure that all required js files are loaded successfully.
If you are using jQuery then in file B call your function in document ready function:
$( document ).ready(function() {
//File A code
});
or in plain JavaScript:
(function() {
// your page initialization code here
// file A code
})();
I want to execute a bit of Javascript code inside my handlebars template. Typically in the application I do this
<script type="text/javascript">
var #Model.JavascriptVariableName;
$(function () {
#Model.JavascriptVariableName = new TagInput()
.withAvailableTags(#Html.Raw(Model.AvailableTagsJson))
.withAppliedTags(#Html.Raw(Model.AppliedTagsJson))
.withMinCharsAutocomplete(#Model.MinCharsAutocomplete)
.allowBackspaceDelete(#Model.DeleteWithBackspace.ToString().ToLowerInvariant())
.allowNewTags(#Model.AllowNewTags.ToString().ToLowerInvariant())
.initialize($('##Model.ElementId'), $('##(Model.ElementId)_hidden'));
#if(Model.OnChangeJavascript.IsNotNullOrEmpty()) {
#:#(Model.JavascriptVariableName).onChange = function () { #Html.Raw(Model.OnChangeJavascript) }
}
});
</script>
But since I am already inside of a handlebars template with I tried to just insert the $(function(){}) that just gets spit out as text which makes sense. So how then can I create a bit of dynamic Javascript inside of handlebars???
<script type="text/x-handlebars-template" id="tagsTemplate">
<div>Tags</div>
var #Model.JavascriptVariableName;
$(function () {
#Model.JavascriptVariableName = new TagInput()
.withAvailableTags(#Html.Raw(Model.AvailableTagsJson))
.withAppliedTags(#Html.Raw(Model.AppliedTagsJson))
.withMinCharsAutocomplete(#Model.MinCharsAutocomplete)
.allowBackspaceDelete(#Model.DeleteWithBackspace.ToString().ToLowerInvariant())
.allowNewTags(#Model.AllowNewTags.ToString().ToLowerInvariant())
.initialize($('##Model.ElementId'), $('##(Model.ElementId)_hidden'));
#if (Model.OnChangeJavascript.IsNotNullOrEmpty())
{
#:#(Model.JavascriptVariableName).onChange = function () { #Html.Raw(Model.OnChangeJavascript) }
}
});
</script>
The above code wont work.
Unless you are using handlebar to compile your webpage server side I don't see why you need to wait every time for the window Ready event: $(function(){}) in fact wrap a function to make it sure it will fire only when the DOM is ready (has been loaded).
You can simply skip that part in your code if you want.
I would strongly discourage to put JS code in a template: why don't you generalize that code and compile with Handlebars some DOM stuff with ids or classes that you can use in your "generic" function instead?
I am using MVC3 with heavy usage of ajax to get Partial Views. If Partial view contains JavaScript then it is added as a new js file as shown in snapshot:
so If I have a js function:
function checkValue(){
//do work
}
on ajax call a new dynamic JS file will be added contained this function and it conflicts with old once.
myfile.js contained:
function checkValue(){
//do work
}
and 1.js (dynamic file) will contain it too
function checkValue(){
//do work
}
So when I call it due to presence in old file it call already present function which is outdated. How to solve this situation like new JavaScript replace old one.
Thanks
You can check whether something is defined and redefine it only if it is not:
var checkValue = checkValue || function () {
//do work
};
If you want your definitions to override each-other instead of defining the function with a name, define them on the global object each time:
window.checkValue = function () {
//do work
};
In trying to namespace my js/jquery code, I have come up against the following problem.
Basically, I used to write all my JS code in each html/php file, and I want to abstract that away to a single js file with namespaces.
So, in my html file I have:
<script type="text/javascript">
$(document).ready(productActions.init());
</script>
And in my js file I have:
var productActions = {
init: function() {
alert('initialsed');
$('#field_id').change(function() {
alert('ok!');
});
}
The productActions init function is definitely running, because I get the first alert (initialised). However, it seems that none of the jquery binding functions do anything at all. Stepping through the init function shows that the above change function is being registered, but actually changing the value in the field does absolutely nothing.
Am I missing something obvious here?
$(document).ready(productActions.init());
This code calls init() immediately and passes its return value to ready(...). (just like any other function call)
Instead, you can write
$(document).ready(productActions.init);
To pass the function itself. Howeverm this will call it with the wrong this; if you need this, write
$(document).ready(function() { productActions.init() });
I have a page where I am using jquery/ajax to pull down a chunk of HTML/JS from another component and injecting it into the page. That HTML references additional JS files, and I need those referenced JS files to be loaded before I run my javascript.
The HTML/JS that is being injected looks something like this:
<script type="text/javascript" src="http://myserver/js/ABunchOfStuff.js"></script>
<div>
blah blah blah
</div>
<script type="text/javascript">
//"sourceList" is defined in the ABunchOfStuff.js above, but it's not available by the time this executes.
$("input#autocomplete").autocomplete({
source: sourceList,
minLength: 2
});
</script>
Normally I would just hook into a window load event or a $(document).ready() or whatever, but in this case the window and document have already been completely loaded, and now we're adding additional content after the fact.
One possiblity would be to put a recursive setTimeout call in that would keep firing until the referneced javascript was available, but that's pretty ugly.
So is there any clean way to trap the event of a referenced javascript has been loaded and to execute code at that time?
Thanks
You can also use getScript and do your autoComplete in the success callback:
jQuery.getScript( 'http://myserver/js/ABunchOfStuff.js', function(data, textStatus) {
$("input#autocomplete").autocomplete({
source: sourceList,
minLength: 2
});
} );
The big question is, how do you inject this script ?
If you using "standard" script tag insertion, you can go and watch for the onload event (onreadystatechange in IE).
var scr = document.createElement('script');
scr.type = 'text/javascript';
scr.src = 'somewhere/somename.js';
scr.onload = scr.onreadystatechange = function() {
if( /complete|loaded/.test(scr.readyState) ) {
// do something
}
else {
// do something
}
};
What you are doing wrong here is not waiting for the DOM to load.
If you change your .autocomplete to only execute once the DOM is loaded through $(document).ready it will have executed the ABunchOfStuff.js
Like this:
(function($) {
$(document).ready(function() {
$("input#autocomplete").autocomplete({
source: sourceList,
minLength: 2
});
}
}(jQuery));
If you control the http://myserver/js/ABunchOfStuff.js file, then you can call your other JS from it when it first executes. Since it executes when it first loads and when it's available, you have the perfect timing.
If this JS file is used other places too, you could add some generic functionality to it for calling a callback when it executes by adding something like this to it:
try {
if (aBunchOfStuffCallbacks) {
for (var i = 0; i < aBunchOfStuffCallbacks.length; i++) {
aBunchOfStuffCallbacks[i].call(this); // call callback to announce we're loaded
}
} catch(e) {}
And, then in any web page where you want to be called when aBunchOfStuffCallbacks was loaded, you would just do this:
var aBunchOfStuffCallbacks = [];
aBunchOfStuffCallbacks.push(myFunc);
function myFunc() {
// put my code here for when aBunchOfStuffCallbacks is loaded
}
This would allow for multiple callbacks. The simpler version for just one callback looks like this:
try {
if (aBunchOfStuffCallback) {
aBunchOfStuffCallback.call(this); // call callback to announce we're loaded
}
} catch(e) {}
And, it would look like this to set it:
var aBunchOfStuffCallbacks = function () {
// put my code here for when aBunchOfStuffCallbacks is loaded
}