Is it possible to call a javascript function when a user has successfully been redirected to a new view?
I have tried the following-
C#
if (Session["UserId"])
{
return RedirectToAction("LoggedIn");
}
cshtml
#if (Session["UserId"] != null)
{
<script>$('#btnTest').click();</script>
}
Fails with
JavaScript runtime error: '$' is undefined
I have referenced jquery in the project and tested it just with a button click and this works however the above is failing. How can I achieve this?
Most likely, this HTML is being inserted before jQuery is actually referenced. For example, if you're not referencing jQuery until the closing body tag (as you should), then this will be run before jQuery actually exists. The best way to handle this type of thing is with sections. You can defined a section in your layout for scripts and then ensure that that section is below required dependencies like jQuery.
_Layout.cshtml
<script src="/path/to/jquery.js"></script>
#RenderSection("Scripts", required: false)
</body>
AwesomeView.cshtml
#section Scripts
{
#if (Session["UserId"] != null)
{
<script>$('#btnTest').click();</script>
}
}
This will result in the following HTML being generated (assuming the session variable isn't null of course:
<script src="/path/to/jquery.js"></script>
<script>$('#btnTest').click();</script>
</body>
It's important to note that this only works with standard views. Partial views cannot define sections, so if you need to include some bit of script inside a partial, you'll have to take a different approach to deferring execution. Generally, the best method at that point is simply utilize window.onload directly:
var onload = window.onload
window.onload = function () {
onload();
// your code here
}
That should ensure that jQuery has already been loaded. However, it's really better to just not include JS code in partials, in the first place. There's a number of issues that can result from that and it makes your code very fragmented and brittle, in general.
Related
On my HTML page, I have the following way of loading JS files:
<script id="jjquery"></script>
<script id="jscookie"></script>
<script id="winx"></script>
<script>
(function(){
if(screen.width > 479)
document.querySelector("#jjquery").setAttribute("src", "/js/jquery-3.3.1.min.js");
document.querySelector("#jscookie").setAttribute("src", "/js/jscookie.js");
document.querySelector("#winx").setAttribute("src", "/js/winx.js");
}());
</script>
Basically I used the way described in this answer to only load certain scripts starting from the specific screen resolution. But the third script out of three, that relies on jquery, is not working and says $ is not defined. Even though if I enter $ in the console, it returns normal response. And when I see the order of the scripts in the HTML output, it is clear that jquery is loaded prior to two other scripts. What could be the issue here?
P.S. everything works comlpetely fine if I just load the scripts normally without the resolution check.
The problem is that the scripts are loaded asynchronously, so the order of your code isn't relevant.
You can see a MDN example of how you can properly import your scripts.
In your case, if some of your scripts depend on jquery, you can create an onLoadJquery function and append it as the onload attribute of the script, this function will be executed after the import.
<script>
(function(){
if(screen.width > 479) {
// add the callback that will be executed once jquery is loaded
document.querySelector("#jjquery").addEventListener("load", onLoadJquery);
document.querySelector("#jjquery").setAttribute("src", "/js/jquery-3.3.1.min.js");
document.querySelector("#jscookie").setAttribute("src", "/js/jscookie.js");
}
function onLoadJquery() {
// safely import the rest of the scripts
document.querySelector("#winx").setAttribute("src", "/js/winx.js");
}
}());
</script>
I have a widget with some custom js:
class ImagePreviewWidget(ClearableFileInput):
...
class Media:
css = {
'all': ('css/image-preview-widget.css',)
}
js = ('js/image-preview-widget.js', )
The custom js uses jQuery, which is loaded independently, so I need to wrap my module initialization in:
window.onload = function() {
cusomJsStart();
};
Which is not very clean (one problem is that I am maybe interfering with other window.onload calls). Is there a better way to load the widget javascript?
EDIT
Just to make it clear: the whole point of this question is that jQuery is not yet loaded when the script runs, and that the script loading order is outside my control.
Instead of setting window.onload you should use addEventListener:
window.addEventListener("load", customJsStart);
(If you need to support IE<9 then some fallback code is required - see the MDN link above).
Even nicer would be if you could tell Django to add a defer attribute to the scripts you pass in the Media class, but it doesn't support that yet.
There is nothing better than pure Javascript ! Alternatively you can use JQuery like this :
$(function(){
cusomJsStart();
});
But with this method your page will be heavier because of JQuery source file loading.
Or you can put your cusomJsStart() at the end of your HTML file, but it's not clean at all !
You did the right choice.
How can I to know if resource file is loaded by a page using Javascript?
For example some .css, js or other sort of sort of file?
It depends on whether you're adding the files dynamically. I'll explain for JavaScript files.
Either use this piece of code to load an external file:
var scriptElem = document.createElement('script');
scriptElem.onload = function() {alert('Script loaded.');}
scriptElem.src = 'test.js';
document.getElementsByTagName('head')[0].appendChild(scriptElem);
Or if you're adding them through HTML, you can make the script file reflect the fact it's been loaded with a variable. E.g. in test.js:
// ...rest of file...
window.testLoaded = true;
You can then check for window.testLoaded === true anywhere on the page.
For CSS files you can also use the first technique, but of course not the second. It might be possible to define a stub definition for a class .test, then create an element with that class and check whether it has a certain style that you set in the CSS file.
Use jQuery.ready event which means that all the page has been setup and then you start executing your JavaScript logic
$(document).ready(function(){
//your code
});
If you have the need to load stuff asynchronously then you should use your JavaScript code to load it like the example of loading a script:
$.getScript("http://.../some.js", function(){
//script loaded and hooked on the page; your custom logic here
});
For anything more complex I'd suggest some dependency management system like requireJS.
What is the best practice of activating jquery ui widgets for html loaded and inserted into the document by ajax?
I am an advocate of unobtrusive javascript and strongly believe that all functionality accessed by javascript should be also accessible without it. So in the ideal case, each form which opens in a popup, should also have its separate page and links to them should be replaced with javascript-based ajax loading.
I find this pattern very useful for loading and inserting a part of another page into the current document:
$('#placeholder').load('/some/path/ #content>*');
Or to make it more generic:
$('a.load').each(function() {
$(this).load($(this).attr('href') + ' #content>*');
});
However, I would also like to activate the javascripts from the dynamically loaded page, so that different widgets function correctly.
I know, I could add those javascripts to the current document and activate all of them in the callback of .load(), or I could use $.get() to get some JSON with html and javascripts separately, but I guess, there might be a more elegant generic solution to this.
What would you recommend?
BTW, I am using Django in the backend.
The question is how you're activating your javascript currently. If you're doing something like:
$(document).ready(function() {
$('a.foo').click(function() { ... });
})
You could consider changin things to:
$(document).ready(function() {
$('a.foo').live('click', function() { ... });
})
That way when new DOM objects are loaded the event handlers are attached.
What I've done is used the "load" option that is specifiable by jquery.ui widgets. Unfortunately, this isn't well documented, so you won't see the option here: http://jqueryui.com/demos/tabs/#options for example, but you will see it here: http://jqueryui.com/demos/tabs/#method-load
For the most part, each of the methods you invoke have an initial option that can be set, which is what prompted me to try using the load.
In my own application, I have 3 levels of nested tabs that are being created dynamically via AJAX. In order to have the javascript for each of the tabs applied dynamically, I have nested load functions that are first initiated when the document is loaded.
So my template file has:
<script type="text/javascript" src="{{ MEDIA_URL }}js/tabs.js"></script>
<script type="text/javascript">
$(function() {
$('.overall_tabs').tabs({
load: initializeOverallTabs
});
});
</script>
My tabs.js file has:
function initializeOverallTabs(event, ui){
...
$('.lvl_two_tabs').tabs({
load: initializeTabLevel2
});
...
}
function initializeTabLevel2(event, ui){
...
// So on and so forth
...
}
Also, I recommend when working inside the loaded areas to make your references be specific to that pane. This was extremely important when working with tabs. The best way I found to do this is below.
In your tabs.js file:
function initializeOverallTabs(event, ui){
$panel = $(ui.panel);
$panel.find('lvl_two_tabs').tabs(...);
}
I found this question strangely coincidental! I recently explained my solution to a few developers to the same situation with the same Jquery/Django Environment. I hope that helped!
One way I decided myself for handling widgets from external pages is parsing the HTML of the other page, searching for scripts and executing them in the current page.
For example, there is a form with autocomplete widget on another page which is loaded and inserted to this page. The autocomplete widget should be activated with specific properties, like available choices:
<script type="text/javascript">
//<![CDATA[
$(function() {
$("#colors").autocomplete({
source: ['red', 'green', 'blue', 'magenta', 'yellow', 'cyan']
});
});
//]]>
</script>
Then in the current page I can have the following script which loads HTML and additionally collects all javascripts within it and executes them:
var oRe = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;
$('#placeholder').load(
'/some/path/ #content>*',
function(responseText, textStatus, XMLHttpRequest) { // <-- callback function
var sScripts = "";
responseText.replace(
oRe,
function($0, $1) {
sScripts += $1;
return $0;
}
);
eval(sScripts);
}
);
One drawback here is that the current document should initially be loading all the libraries which might appear in the included forms. For example, in this case, it would be the jquery-ui including the autocomplete widget. I guess I could extend the code by searching for script tags which load external scripts and loading them in the current document if they are not present.
I have a JS script that will be hosted on my server and that others will embed in their html, i.e.
...<code for http://yoursite.example.com />
<script type="text/javascript" src="http://mysite.example.com/awesome.js" />
...<code for http://yoursite.example.com />
My script declares an object with a bunch of properties accessible for use as a Javascript Object(), i.e.
<script type="text/javascript">
//From http://mysite.example.com/awesome.js
alert(Awesome.Name);
</script>
Due to the load time variance, it seems I need to signal that the "Awesome" object in my script is ready. I need this to stand on its own, so no dependencies on specific JS frameworks.
Do I need to publish my own custom JS event, or does the simple fact that my script is loaded get captured by one of the existing page-level events? How else should I be doing this?
UPDATE: as a point of reference, if I include the JS in an HTML page running from http://mysite.example.com, the Awesome object is available and populated. When the JS file is included from another domain, the object is undefined at runtime.
The javascript content of <script> tags is executed procedurally, so this
<script type="text/javascript" src="http://mysite.com/awesome.js" />
<script type="text/javascript">
alert(Awesome.Name);
</script>
will only alert the contents of Awesome.Name if found in any previous script tag.
To understand if everything has been fully loaded on the page, you have to use the DOMContentLoaded, for firefox, and "onreadystatechange" for ie. Also you can simply check the load event on the window object if you don't care about checking the DOM (could be easier).
if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", function(){
doSomething();
}, false );
} else if ( document.attachEvent ) { // IE
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" ) {
doSomething();
}
});
}
If your script needs the DOM to be loaded before your object is instantiated, you should look at some frameworks and see how they handle this, then implement it in your code. If you don't need the DOM to be loaded, then I would let the user worry about the timing of using your object based on when it is loaded. Generally, your object should be available to be be used as soon as your script has been loaded, which means that the object ought to be available right after the script tag that includes it.
Anything accessible in the global scope can be accessed through the window scope. Hence, you could use this:
if (window["Awesome"] != null) { /* do something */ }
A). You realise that script requests are blocking? Are you ok with this or do you want to work around that, because the answer to your question depends on it.
B). Bare bones bulletproof and simple mechanism is to call a specified method which you can guarantee exists on the page. Let that methods implementation be up to the user to do what it will. Lots of other ways exist but we'd need to know what exactly the lifecycle and intent of your code is to recommend anything.
you can simply use it like this:
<script type="text/javascript" src="myfunction.js"></script>
<script type="text/javascript" src="iwannaloadthis.js?onload=executeThis"></script>
remember as pointed out earlier, execute this must be defined in myfunction.js (or before trying to load iwannaloadthis.js.
Hope this helps!!