I have an ASP.NET application using a master page. I am adding a reference to the JQuery library in the master page however there are some content pages and user controls that reference the JQuery library directly. Will I need to remove each reference from those pages or can I leave them in place even though I am adding a reference into the master page of the application?
If you're loading jQuery with the Script Manager, it should load only once.
If you still want intellisense, you can use the following trick:
<% if (false) { %>
<script src="....... script tag here
<% } %>
You should remove them, including the library twice has many side-effects, wiping out any plugins defined for example. It'll load fine, but you'll start getting .pluginMethod is not defined, etc.
To avoid the headaches, only include them once, or register the script with the same key, and let ASP.Net do the include, and with the same key, it'll do so only once.
You will need to remove them I'm afraid. Came across this issue myself and the javascript will break.
Related
What is the correct way to use page-specific JavaScript in Rails 6 (with turbolinks enabled)?
The articles I have found seem to suggest just putting everything in application.js - but this seems messy as it affects every page (and you also need to check the element you want to manipulate exists every time, then add an eventlistener or do nothing at all if it doesn't exist).
Another suggests adding your own folder, putting your custom JS in there and then requiring it from application.js (in effect, the same as above - it's just slightly more clean to look at but everything is still loaded for every page).
What I'd like to achieve is have my JavaScript split and only include it when needed. However, if using 'javascript_pack_tag' in a view to pull it in, this causes turbolinks to get quite upset and keep adding the event listeners, over and over. If you put 'javascript_pack_tag' in the page head, this then invalidates the cache and stops turbolinks from working.
In an ideal world;
The JavaScript would be in it's own custom location
It would be required only for the views that needed it
It would work with turbolinks
This is a burden Turbolinks brings to the stake. When we need to use the full potential of Turbolinks (or Turbo nowadays), we need to control the state of transformations. Meaning we need to bind/unbind (connect/disconnect) javascript events or html elements.
That's probably why the same team from Turbolinks created Stimulus. You can go this way and split all your JS by Stimulus controller and page. Then use javascript_packs_with_chunks_tag at application.html.erb (no need to import or javascript_tag anywhere else). Basically, Rails Webpack can splits your files to be per-page even though everything is imported at application.js file, but the controllers must be imported normally at application.js using import('controllers'). This way Webpacker can splitchunk files and reduce the bundle. Read more about Webpacker chunks and use something like webpack-bundle-analyzer, this helped me a lot to understand Webpacker and javascript imports.
But, even with Stimulus, we need to be idempotent, to avoid duplication of events or elements. And unfortunately you will need to check if element exists, or check if turbolink is caching.
Here goes some things I use to avoid duplication:
// Use at Stimulus `initialize()` or `connect()` or at `turbolinks:load` event, example:
$(document).on("turbolinks:load", () => {
// Avoid loading things again when turbolinks is previewing
if (document.documentElement.hasAttribute("data-turbolinks-preview")) return;
// Sometimes you'll need to check if plugin is already active before trying to initialize
if (table.getData().length) return;
// then starts plugin or your code
table.init();
});
// Use at Stimulus `disconnect()` or at `turbolinks:before-cache` event, example:
$(document).on("turbolinks:before-cache", () => {
// before caching we destroy stuff to avoid duplication
table.destroy();
});
And this post approach can work without Stimulus. Then you can use your custom javascript location, with Turbo and splitting code, as you wanted!
I have some simple JQuery / Javascript to perform some simple logic for all external hyperlinks:
<script>
$("a[href^='http://']:not([href*='"+location.hostname+"']),[href^='https://']:not([href*='"+location.hostname+"'])")
.addClass("external")
.attr("target","_blank")
.attr("title","Opens new window").click(function(e) {alert('You are leaving mysite and going somewhere else, you crazy dude')});
</script>
This is fine for one page. However, I wish to have this in every web page in my application and be 100% sure that it is there.
Is there any good trick to do this?
The only one I can think of is if you are using a java architecture, to have a base JSP and ensure the base JSP calls this.
Any better ideas?
You don't need some server side framework... If you use some templating library (jade handlebars, mustache, jquery templates) or if you simply separate out your HTML files you can pull them each in with jquery and render them on the page. Check out the .load function.
Also, you should separate out your html pages even if they are static.
Wrap it in a function and call the function. Then you can just call the function and leave the implementation to the function call.
Since you didn't specify a server side technology such asp.net or php of course there would be other options (partial views or templates) using that.
function doStuff(){
$("a[href^='http://']:not([href*='"+location.hostname+"']),
[href^='https://']:not([href*='"+location.hostname+"'])")
.addClass("external")
.attr("target","_blank")
.attr("title","Opens new window").click(function(e) {alert('You are leaving mysite and going somewhere else, you crazy dude')});
}
<script>
doStuff();
</script>
This depends on how your site is structured. If you've got a server-side framework (like your JSP example), then you can have a function that makes sure that the script somehow gets included.
If you just have static HTML pages, my recommendation would be to put that code in a script file (let's say dontleaveme.js). Then on every page, just do
<script src="dontleaveme.js"></script>
A good application design will have a layout file or header and footer files which are used on every page. Then it is easy to make changes, such as adding a script, which affect every page on the site. If you are not using this technique, this is a great reason to start.
I was under the impression that you could put javascript in a view template in Rails 3. For example, if I had this html in views/public/home.html.erb
<div id="block">click</div>
then in views/public/home.js.erb, I thought I could put the following javascript, and then click on html to trigger the javascript. However, when I tested it, I got no results. But if I put the javascript in assets/javascript/application.js, then everything worked fine...Shouldn't it also work if it was in a js template with the same name as the html view?
$(document).ready(function(){
test();
});
function test() {
$("#block").click(function() {
$('#block').hide();
});
}
Ummm. no. It just doesn't work that way. Only one of views/public/home.* will be rendered, depending on the responds type.
Javascript shouldn't be added as a view file (bla.js.erb). They must be put in assets/javascripts or at least in lib or vendor directory.
You must also require them in your application.js file, if you already don't use require_tree.
In this way you won't need to reference the javascript in any way in your view (the application.js will include it for you). Otherwise, you need to specify a layout to insert javascript files in block, because views are rendered after tag.
There are a lot of reason not to put javascript directly in html (except for tests obviusly), read the rails asset pipeline for more information.
When you create a view with a different extension from html.erb that will be used only if your url specify a format with that extension, for example mywebsite/users.json will return eventually a index.json.erb.
For AJAX you would like to return a JSON object, not javascript which is definitely not a correct approach. Remember that you are using a framework and you should follow it's guidelines. If you want to live it's rails, it will be hard to work with it.
You can use javascript_include_tag
If you have the js files source1.js, source2.js in public/javascript then you can include them using
javascript_include_tag 'source1', 'source2'
For a Rails 3.1 app, some of my site wide JavaScript is only included when certain real time, instance specific conditions are met. This means I can't put it in the new asset pipeline's application.js because that isn't parsed by erb for embedded Ruby within the current context. Basically, I'm including keyboard shortcuts, based on the current_user that is logged in.
My question: where should this JavaScript with embedded code go, so that the embedded Ruby is still parsed for each page access with the proper context (i.e. current, logged in user)?
The answer seems to just be to put it in the application.html.erb layout view at the bottom, but this seams like I'm hiding away javascript code in a non intuitive location.
I've tried creating an application2.js.erb file, but then I got errors about undefined variables, which I think might be because the asset engine only parses this file once before the output is cached and the scope isn't correct yet for things like current_user.
So, using application.html.erb works just fine here, and this isn't so much a question of how to get it to work functionally. Instead, I'm wondering if there's a more elegant way to incorporate the asset pipeline model here with my requirements and still keep most of my JavaScript in the assets/javascripts directory.
You should try to create app/assets/javascripts/application2.js.erb (or whatever better name you come up with)
And then put something like this in your app/assets/javascripts/application.js:
//= require application2
And then you can have
<%= javascript_include_tag 'application2' %>
wherever you want - for example in your application.html.erb.
Btw, if you want to customize what's included on a per-view basis you might find content_for useful. Check out this screencast
Ok, about unobtrusive js. It will be just a cocept (HAML):
In your view somewhere
# hotkeys are "Ctrl+C", "Ctrl+A"
-current_user.hotkeys.each do |hotkey|
%hotkey{ "data-key" => hotkey.key, "data-behavior" => hotkey.fn }
Then in your application.js
$(document).ready(function(){
if($("hotkey").length > 0){
$("hotkey").each{function(this){
key = $(this).data("key");
fn = $(this).data("behavior");
$(document).bind('keydown', key, fn);
}}
}
})
So just the same JS will extract from HTML hotkeys data and then bind it.
As some people have pointed out, the two options are:
Put your javascript inside the view (and as you say, this doesn't feel quite right).
Put it in a javascript file. Make a conditional inside your view that includes this javascript file if certain conditions are met.
If you need to pass more instance variables from the controller to your javascript, this gem called gon can make your life easier.
This allows you to use the default asset pipeline using the following javascript:
if(gon.conditional){
//your embedded js code here
}
If you want to know more about this gem, checkout this railcast where everything gets explained.
I recently read that for a faster web page load it's a good practice to put the JavaScript links at the end. I did, but now the functions of the referenced file doesn't work. If I put the link at the beginning of the page, everything is fine.
Does this thing of putting JavaScript at the end work only under certain circumstances?
I went through some testing with this as well. If you are loading a Javascript file it is faster to put it at the end BUT it does come with some important caveats.
The first is that doing this often made some of my visual effects noticeable. For example, if I was using jQuery to format a table, the table would come up unformatted and then the code would run to reformat it. I didn't find this to be a good user experience and would rather the page came up complete.
Secondly, putting it at the end made it hard to put code in your pages because often functions didn't exist yet. If you have this in your page:
$(function() {
// ...
});
Well that won't work until the jQuery object is defined. If it's defined at the end of your page the above will produce an error.
Now you could argue that all that styling code could be put in the external file but that would be a mistake for performance reasons. I started off doing that on a project and then found my page took a second to run through all the Javascript that had been centralized. So I created functions for the relevant behaviour and then called the appropriate ones in each page, reducing the Javascript load run time to 50-200ms.
Lastly, you can (and should) minimize the load time of Javascript by versioning your Javascript files and then using far-futures Expires headers so they're only loaded once (each time they're changed), at which point where they are in the file is largely irrelevant.
So all in all I found putting putting the Javascript files at the end of the file to be cumbersome and ultimately unnecessary.
You do have to pay attention to the ordering, but libraries like JQuery make it easy to do it right. At the end of the page, include all the .JS files you need, and then, either in a separate file or in the page itself, put the Jquery calls to act on the page contents.
Because JQ deals with css-style selectors, it's very easy to avoid any Javascript in the main body of the page - instead you attach them to IDs and classes.
This is called Unobtrusive Javascript
Every Yahoo YUI example file I remember has almost all the JavaScript at the end. For example,
Simple Event Handling
Basic Drag and Drop
JSON: Adding New Object Members During Parsing
It looks like Yahoo Practice is roughly "library code at the beginning of <body>, active code at the end of <body>."
Beware, though, this may result in the Flash of Unstyled Content syndrome.