I have a single template file which renders pages that look very similar, but behave a bit different. I have a header and a few text boxes, which are filled by the template language, and there is a canvas, whose content actually differentiates the pages. It basically comes down to calling a different JavaScript function based on the page I am on, and I can't really think of an elegant (!) way to do that.
I have a single JavaScript file that uses jQuery's document ready callback to add interactivity to the site, and also to do the drawing in the canvas. For that, I have to call a specific drawing function based on which page I am on, but I don't know it at that point.
This is certainly not impossible, I can in fact think of several different solutions, but don't really like any of these. The script file is not run through a template engine, so I can't employ any server-side logic there. I could parse the URL in the script, but this sounds like a hack to me. I could also set a variable in a script block within the template file, set a hidden field's value or something like that, but I don't think this logic belongs into the template either.
So how is this usually done?
You can give the <body> tag a "class" value, and your JavaScript code can use that (and other similar cues) to know what sorts of behaviors to add.
For example, your code could do something like:
if ($('body').hasClass('image-gallery')) {
// ... initialize image gallery code ...
}
if ($('body').hasClass('shopping-cart-summary')) {
// ... whatever ...
}
Of course it doesn't have to be just the <body> tag. Your <canvas> could also get a "class" value (and/or "data-foo" attributes) to convey information to your code.
You can embed any JavaScript you want on to a web page, built with the same server-side logic you would build the page with. So you can set global variables or (preferably) invoke functions from your .js files with parameters that contain page-specific logic. Just supply the scripts between <script> tags.
It is not clear if you want to find the page you are on, or the location in the DOM of a given page. For the former, use location.href, or location.pathname to avoid the parsing.
Related
I have a website that has a fixed menu, header, etc. and loads the main content area via AJAX based on the menu clicks.
These "pages" rely on a lot of Javascript and CSS which are individual for each page. Since a user can potentially visit many pages, I want to unload the page-specific JS and CSS. The CSS was straight-forward, but removing the <script> tags does not remove the loaded Javascript (I can still call functions defined in it).
How can I really "remove" the JS? I don't want the memory overhead, or to have to worry about name clashes between pages.
You can load your functions into a page specific object. This will require you to make all your page specific calls though that object. When you want to unload the page, make that object point to something else (say null). Now you don't have references to the object, and it will be cleaned up by the runtime. (Assume nobody else points to it. This is trivial to enforce.)
My understanding (from this post on Perfection Kills) is that you can only completely remove javascript function declarations if they were created using eval(). If that is the case, you can remove the function by calling
delete funcName;
If the function was not created using eval(), the best you can do is redefine it. There are numerous examples of that. Here's one: javascript function redefinition
I have tried many different ways to organize the JavaScript that is specific for each View now, but I have not found any way that I feel comfortable with. It seems messy anyway. Probably (and hopefully) that is because I haven't been working with JavaScript very long, and there is a good way of doing this.
Currently what I'm doing is this:
In my Layout file, I have the a RenderSection for scripts in addition to RenderBody. This section contains all JavaScript relevant for each single view. The global script is tucked away in it's own file.
Inside this sections there is a lot of different part of JavaScript (for my biggest View currently there is about 600 lines of JavaScript):
Some definitions of variables and setting different settings (jQuery settings among others).
Hiding different DOM elements on the screen that will be made visible when the user interacts with the View later on.
A lot of jQuery code for different events linked to DOM elements ( click/keyup++ )
Some code refactored into methods because they are used by different jQuery events.
The things I don't like here are mainly two things:
All this code is put into one big codeblock and it's hard to find the parts of the script I'm looking for. Basically, it gets quite unmaintainable as the script grows.
The script is located in the same file as the View. I would like to put the script into a seperate file, but I can't since I use different parts of my Model, and some HtmlHelpers inside the script as well. For example, where I execute some $.post calls, I use #Url.Action('...') to link it to the correct action method to be sure that my script will continue to work even if I change my routing.
I also use the Model to decide if some elements should start out hidden or not like this (is this an ok way to make it start out hidden, or is there a better way? It seems kind of hacky even if I can't put my finger on it).:
code
#if( Model.SomeBoolValue ){
#:$("#DOMelementID").hide();
}
Some pointers to get me in the right direction would be highly appreciated. I need to get this better structured before I lose control over my own code.
I would recommend you taking a look at jQuery plugin authoring and organize your javascript code in terms of plugins that you will attach to your DOM elements.
As far as the #Url.Action('...') problem is concerned there are many ways to solve this issue and externalize your scripts into separate files. For example let's suppose that you are AJAXifying an existing form or ActionLink which already contains the url:
$('#myAnchor').click(function() {
// use this.href to fetch the url
$.post(this.href, function(result) {
});
return false;
});
Now if you simply wanted to send an AJAX request when a user clicks on a div for example, you could always use HTML5 data-* attributes (the same way ASP.NET MVC 3 unobtrusive AJAX and validation works) to define this url on the DOM element:
<div id="mydiv" data-url="#Url.Action("Some Action")">click me</div>
and now in a separate file
$('#mydiv').click(function() {
var url = $(this).data('url');
$.post(url, function(result) {
});
});
and if you follow my first advice about writing a jQuery plugin your code will look like this:
$('#mydiv').myplugin();
Now let's consider the following snippet:
#if(Model.SomeBoolValue) {
#:$("#DOMelementID").hide();
}
So from what it seems here you are using the view model properties to show/hide sections of your code. OK, here's what I may suggest you: JSON serialize this model into the view and then you can start passing values from it to your newly developed jQuery plugins:
<script type="text/javascript">
var model = #Html.Raw(Json.Serialize(Model));
// now configure configure your plugins, for example
$('#mysection').myplugin({ model: model });
</script>
Now inside your plugin you will have access to absolutely all properties of your view model.
And that's all that you will need to put in your view. All the other javascript will be of course in separate javascript files, properly organized in reusable jQuery plugins.
Yep, it can get tough.
Here's what we do, and works for us (in bold, because it may not work for you).
For each View/page, we work out what model properties are required by the JavaScript in order to make decisions (a.k.a "logic").
We also have a section in the Layout for the JavaScript.
We then set a single JavaScript property in the View/page, encapsulating these properties, something like this:
#section JavaScript {
<script type="text/javascript">
yn.yp = {
someBoolValue: #Model.SomeBoolValue,
someOtheProp: '#Model.SomeOtherProp'
}
</script>
}
yn = your namespace, tying the global namespace for your project/company.
yp = your page that your setting the JS property for.
Then in your external JS file:
$(function() {
if (yn.yp.someBoolValue) {
$("#elementid").hide();
}
});
This is also a very clean way to handle routing URL's to be used by client-side AJAX. Setup a property like yn.yp.urls, and set the URL's in there in the View, then the JS can access them easily and without any hard-coding whatsoever.
Overall, the goal here is to reduce server-side code in the embedded page JavaScript.
Set properties for whatever the JS needs to make decisions, then let the JS make the decisions itself.
Hope that makes sense.
Based on a click event on the page, via ajax I fetch a block of html and script, I am able to take the script element and append it to the head element, however WebKit based browsers are not treating it as script (ie. I cannot invoke a function declared in the appended script).
Using the Chrome Developer Tools I can see that my script node is indeed there, but it shows up differently then a script block that is not added dynamically, a non-dynamic script has a text child element and I cannot figure out a way to duplicate this for the dynamic script.
Any ideas or better ways to be doing this? The driving force is there is potentially a lot of html and script that would never be needed unless a user clicks on a particular tab, in which case the relevant content (and script) would be loaded. Thanks!
You could try using jQuery... it provides a method called .getScript that will load the JavaScript dynamically in the proper way. And it works fine in all well known browsers.
How about calling eval() on the content you receive from the server? Of course, you have to cut off the <script> and </script> parts.
If you're using a library like jQuery just use the built-in methods for doing this.
Otherwise you'd need to append it to the document rather than the head like this:
document.write("<scr" + "ipt type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js\"></scr" + "ipt>");
In all honesty, I have no idea why the script tag is cut like that, but a lot of examples do that so there's probably a good reason.
You'll also need to account for the fact that loading the script might take quite a while, so after you've appended this to the body you should set up a timer that checks if the script is loaded. This can be achieved with a simple typeof check on any global variable the script exports.
Or you could just do an eval() on the actual javascript body, but there might be some caveats.
Generally speaking though, I'd leave this kind of thing up to the browser cache and just load the javascript on the page that your tabs are on. Just try not to use any onload events, but rather call whatever initializers you need when the tab is displayed.
In the 1990s, there was a fashion to put Javascript code directly into <a> href attributes, like this:
Press me!
And then suddenly I stopped to see it. They were all replaced by things like:
Press me!
For a link whose sole purpose is to trigger Javascript code, and has no real href target, why is it encouraged to use the onclick property instead of the href property?
The execution context is different, to see this, try these links instead:
Press me! <!-- result: undefined -->
Press me! <!-- result: A -->
javascript: is executed in the global context, not as a method of the element, which is usually want you want. In most cases you're doing something with or in relation to the element you acted on, better to execute it in that context.
Also, it's just much cleaner, though I wouldn't use in-line script at all. Check out any framework for handling these things in a much cleaner way. Example in jQuery:
$('a').click(function() { alert(this.tagName); });
Actually, both methods are considered obsolete. Developers are instead encouraged to separate all JavaScript in an external JS file in order to separate logic and code from genuine markup
http://www.alistapart.com/articles/behavioralseparation
http://en.wikipedia.org/wiki/Unobtrusive_JavaScript
The reason for this is that it creates code that is easier to maintain and debug, and it also promotes web standards and accessibility. Think of it like this: Looking at your example, what if you had hundreds of links like that on a page and needed to change out the alert behavior for some other function using external JS references, you'd only need to change a single event binding in one JS file as opposed to copying and pasting a bunch of code over and over again or doing a find-and-replace.
Couple of reasons:
Bad code practice:
The HREF tag is to indicate that there is a hyperlink reference to another location. By using the same tag for a javascript function which is not actually taking the user anywhere is bad programming practice.
SEO problems:
I think web crawlers use the HREF tag to crawl throughout the web site & link all the connected parts. By putting in javascript, we break this functionality.
Breaks accessibility:
I think some screen readers will not be able to execute the javascript & might not know how to deal with the javascript while they expect a hyperlink. User will expect to see a link in the browser status bar on hover of the link while they will see a string like: "javascript:" which might confuse them etc.
You are still in 1990's:
The mainstream advice is to have your javascript in a seperate file & not mingle with the HTML of the page as was done in 1990's.
HTH.
I open lots of links in new tabs - only to see javascript:void(0). So you annoy me, as well as yourself (because Google will see the same thing).
Another reason (also mentioned by others) is that different languages should be separated into different documents. Why? Well,
Mixed languages aren't well supported
by most IDEs and validators.
Embedding CSS and JS into HTML pages
(or anything else for that matter)
pretty much destroys opportunities to
have the embedded language checked for correctness
statically. Sometimes, the embedding language as well.
(A PHP or ASP document isn't valid HTML.)
You don't want syntax
errors or inconsistencies to show up
only at runtime.
Another reason is to have a cleaner separation between
the kinds of things you need to
specify: HTML for content, CSS for
layout, JS usually for more layout
and look-and-feel. These don't map
one to one: you usually want to apply
layout to whole categories of
content elements (hence CSS) and look and feel as well
(hence jQuery). They may be changed at different
times that the content elements are changed (in fact
the content is often generated on the fly) and by
different people. So it makes sense to keep them in
separate documents as well.
Using the javascript: protocol affects accessibility, and also hurts how SEO friendly your page is.
Take note that HTML stands for Hypter Text something something... Hyper Text denotes text with links and references in it, which is what an anchor element <a> is used for.
When you use the javascript: 'protocol' you're misusing the anchor element. Since you're misusing the <a> element, things like the Google Bot and the Jaws Screen reader will have trouble 'understanding' your page, since they don't care much about your JS but care plenty about the Hyper Text ML, taking special note of the anchor hrefs.
It also affects the usability of your page when a user who does not have JavaScript enabled visits your page; you're breaking the expected functionality and behavior of links for those users. It will look like a link, but it won't act like a link because it uses the javascript protocol.
You might think "but how many people have JavaScript disabled nowadays?" but I like to phrase that idea more along the lines of "How many potential customers am I willing to turn away just because of a checkbox in their browser settings?"
It boils down to how href is an HTML attribute, and as such it belongs to your site's information, not its behavior. The JavaScript defines the behavior, but your never want it to interfere with the data/information. The epitome of this idea would be the external JavaScript file; not using onclick as an attribute, but instead as an event handler in your JavaScript file.
Short Answer: Inline Javascript is bad for the reasons that inline CSS is bad.
The worst problem is probably that it breaks expected functionality.
For example, as others has pointed out, open in new window/tab = dead link = annoyed/confused users.
I always try to use onclick instead, and add something to the URL-hash of the page to indicate the desired function to trigger and add a check at pageload to check the hash and trigger the function.
This way you get the same behavior for clicks, new tab/window and even bookmarked/sent links, and things don't get to wacky if JS is off.
In other words, something like this (very simplified):
For the link:
onclick = "doStuff()"
href = "#dostuff"
For the page:
onLoad = if(hash="dostuff") doStuff();
Also, as long as we're talking about deprecation and semantics, it's probably worth pointing out that '</a>' doesn't mean 'clickable' - it means 'anchor,' and implies a link to another page. So it would make sense to use that tag to switch to a different 'view' in your application, but not to perform a computation. The fact that you don't have a URL in your href attribute should be a sign that you shouldn't be using an anchor tag.
You can, alternately, assign a click event action to nearly any html element - maybe an <h1>, an <img>, or a <p> would be more appropriate? At any rate, as other people have mentioned, add another attribute (an 'id' perhaps) that javascript can use as a 'hook' (document.getElementById) to get to the element and assign an onclick. That way you can keep your content (HTML) presentation (CSS) and interactivity (JavaScript) separated. And the world won't end.
I typically have a landing page called "EnableJavascript.htm" that has a big message on it saying "Javascript must be enabled for this feature to work". And then I setup my anchor tags like this...
<a href="EnableJavascript.htm" onclick="funcName(); return false;">
This way, the anchor has a legitimate destination that will get overwritten by your Javascript functionality whenever possible. This will degrade gracefully. Although, now a days, I generally build web sites with complete functionality before I decide to sprinkle some Javascript into the mix (which all together eliminates the need for anchors like this).
Using onclick attribute directly in the markup is a whole other topic, but I would recommend an unobtrusive approach with a library like jQuery.
I think it has to do with what the user sees in the status bar. Typically applications should be built for failover in case javascript isn't enabled however this isn't always the case.
With all the spamming that is going on people are getting smarter and when an email looks 'phishy' more and more people are looking at the status bar to see where the link will actually take them.
Remember to add 'return false;' to the end of your link so the page doesn't jump to the top on the user (unless that's the behaviour you are looking for).
I have been tasked with improving the current mess that is our JavaScript "strategy"; we're an online shopping company and my boss has given me time to do this properly. He is very keen on keepin this modular and increase the reusability of the components.
Our HTML is being rendered with JSP and we have lots of custom tags writing out, for example, information about products without the web designers needing to worry about it.
Now, we want to do similar things with JavaScript. The web designers should be given a set of custom tags, like, say,
<foo:draggable>
... some HTML here ...
</foo:draggable>
that will wrap the HTML in a <div> with a drag bar at the top and a close button.
My idea is to mark the div with a unique namespaced CSS class name, like foo_draggable, and then put all my functions in a single JS file. That JS file then sees if there are elements with the CSS class foo_draggable in the DOM and if it finds any it will attach the required event handlers.
However, I am worried about scaling problems, and wondering whether it is a good idea to have lots of selector queries running when they quite often aren't going to be used.
The first alternative would be to initiate each draggable item explicitly but that would mean putting <script> tags all over the place. The second approach would be to not put all UI function in one file but rather just download the ones I need, but that would mean lots more HTTP requests and slower page load speed.
Has anyone got experience with this?
What about having two classnames?
<div class='foo fooDragable'></div>
<div class='foo fooSortable'></div>
You add the class 'foo' to all your elements that require javascript modification.
Your javascript has to check the dom only once for foo.
var $foo = $('.foo');
Afterwards you can search within this array which should be way smaller than the complete dom.
var $dragAble = $foo.filter('.fooDragable');
Have you considered or taken a look to JSF? I know it's a major change if you aren't using JSF yet. But there are lot of ready-to-use JSF component libaries with an ajaxical sauce, for example RichFaces, IceFaces, PrimeFaces, etc. It's almost a waste of time to create components/tags for it yourself.
Alternatively you can replace all Javascripts to use the great jQuery JS framework.
Depending on how many separate components you have, the extra overhead of running the selectors might not be a big deal. You can initialize all the components just the once, when the page is loaded. Anything that's not present on the page simply won't get initialized, and will incur no further overhead. In most JavaScript frameworks, selecting by classname (or tag name) is pretty fast. It's only the complex selectors, which aren't natively supported by the browser, that are slow.
If you have a few commonly used components, and then a set of less commonly used ones, it may be worth splitting those up. Keep the commonly used components in a single JavaScript file (minified, served with compression and aggressive caching), and load that in every page, regardless of whether it's needed or not. Caching will ensure it's only downloaded once, and it'll only be one small HTTP request. For the less common components, keep them in separate files (ideally, one per component), and add a script tag on pages that use them.
I'm not entirely familiar with how JSP works, but it might be possible to do this automatically - if a tag is included in the document, add a script tag for foo_widget.js in the document header, or something like that.