4

I have been reading about some good practices in JavaScript and one of them was Unobtrusive JavaScript. The first point caught my attention

Separation of functionality (the "behavior layer") from a Web page's structure/content and presentation

On the wiki page, one of the examples is that you should bind actions to events in JS files instead of in the HTML. This example

<input type="text" name="date" id="date" />
...
window.onload = function() {
    document.getElementById('date').onchange = validateDate;
};

is favored over

<input type="text" name="date" onchange="validateDate()" />

However, I would say I'd prefer the second code with onchange attribute over the first one. My reasons for this are

  • It is easily readable and immediately clear what will happen on change (or any other event) on that element.
  • I don't have to got through JavaScript files and look where (and whether) the onchange event is bind and whether there are some other events such as click defined for #date.
  • Frameworks such as AngularJS have ng-click and is mixing up HTML structure with JS. Why shouldn't I?

The disadvantages of not using unobtrusive javascript that I have read about are

  • Polluting global namespace.
  • Creating long and unreadable inline code.
  • If the code for an event changes, you have to change it only in one place - that is in a JS file.

But I think that the disadvantages can be solved.

  • Instead of polluting namespace, create the app in one variable so the code would look like onchange="app.validateDate()" and no polluting would happen.
  • Inline code would not be written and would be separated in a function in JS file, then it'd be called like onclick="app.action();".
  • Isn't it the same as using a function in onclick attribute? Because in the end you have to make a change just in one function in both approaches whether it is $('input').change(function () {/* ... change ... */}); or app.action = function () {/* ... change ... */}.

So is it actually still considered a good practice?

Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137
leopik
  • 2,323
  • 2
  • 17
  • 29
  • 4
    The main reason we'd separate this is because that way, making changes will be a lot easier in the future. You don't want to change the name of your onChange function and then have tyo go through all your HTML to change it. You also want people to be able to use your site without javascript, so tying them in _only when there is javascript_ is good practise. It also keeps your code simpeler, as you separate everything. Just like an external CSS file is easier, so you can swap files to change styling, swapping JS files allows you to do the same with interactions. – somethinghere Jun 08 '15 at 13:13
  • 1
    Besides, not all browsers implement the same events for all elements. You might have browser-specific implementations, for instance. – Octav Zlatior Jun 08 '15 at 13:14
  • 1
    Lastly, this is also good practise because it's _common practise_. Any other developer going to work with your code? They know what to expect. It is largely a convention. See also: _'And thirdly, the code is more what you'd call "guidelines" than actual rules. Welcome aboard.'_ - Captain Barbossa – somethinghere Jun 08 '15 at 13:16
  • Also, you might want to be able to add or remove events dynamically, etc... then, libraries such as jQuery and jQueryUI implement their own events and distinct ways of selecting and interacting with elements. It's just a matter of abstraction in the end: separation brings more flexibility and also more consistency. – Octav Zlatior Jun 08 '15 at 13:16
  • 1
    About consistency, some things can be implemented only if you attach events from JS code. So, you would have two ways of dealing with events, one where you define them as attributes and another where you attach them from JS code - inconsistency is bad when you are later trying to read the code and for the general design of the code. – Octav Zlatior Jun 08 '15 at 13:18
  • 1
    +1 - *Frameworks such as AngularJS have ng-click and is mixing up HTML structure with JS. Why shouldn't I*? – Veverke Jun 08 '15 at 13:19
  • 2
    Although I would agree that the second code is shorter and maybe nicer, separating JavaScript from HTML is better practice. Imagine you work in a team and you are responsible only for JavaScript programming. It is other persons who create and edit the HTML file. And there are others who take care for CSS. By separation of concerns each team can concentrate on its own part without interfering in the affairs of the others. – cezar Jun 08 '15 at 13:19
  • 1
    I don't know what to say about angular js... This is one feature I never liked about it, but I guess you can argue that it's not really javascript - it's attributes the framework uses for pre-parsing of the HTML. Afterwards, guess what: the events are attached from JS land (and not added to the HTML code) :) – Octav Zlatior Jun 08 '15 at 13:22
  • @somethinghere I haven't though about the change of `onchange` action function name - I guess it'd be a hassle if I had in my code 5x defined `onclick="app.showFirstName();"` but suddenly I'd need to show last name on click. Regarding your second point, if the javascript is turned off, in both cases the action won't be triggered so i don't see why this is a problem. And the simplicity of the code - this is the point I think is arguable - I'd rather say it is simpler to see directly what happens on click then opening extra files. – leopik Jun 08 '15 at 13:25
  • @somethinghere Regarding your second comment, that is a good point and I agree that it is better to use a common practice just for the sake of team work. – leopik Jun 08 '15 at 13:27
  • AngularJS should be viewed not as HTML but as AngularJS, IMO. HTML itself should be marked up semantically, and projects should follow a reasonable convention that allows developers to find functionality as easily as possible. – Dave Newton Jun 08 '15 at 13:31

1 Answers1

3

This is a very broad topic and heavily opinion based. There is no one answer to everything. However, here are some observations:

  1. You are polluting the namespace whatever you do. app.validateDate pollutes the namespace just as validateDate does, just by virtue of needing to have a globally accessible function name. In complex modern sites, there are tons of scripts competing for global names. Ideally you're never exposing any name globally, not even a namespace name.

  2. .onclick = handler is not great either. You'd want:

    document.getElementById('date').addEventListener('change', function () { .. });
    

    This is even less obtrusive and allows several scripts to bind event listeners to the same element. Again, in complex modern sites one of the highest priorities you can have is to ensure nobody is stepping on anyone else's feet. You never know who else might be interested in the change event of that element in the future.

  3. It is still more code to write it inline than elsewhere. Longer HTML code that is. HTML can already be very verbose. Anything you can move elsewhere you should. Reducing the amount of code in any one particular file is an art in itself and important for readability. Yeah, it's "just one more attribute"... on top of all the other attributes and elements and inline declarations you're also not avoiding. It's just piling up, and that's how code gets messy and unreadable and unmaintainable.

  4. Reusability: document.getElementById(..).addEventListener can be written once in an external file and automagically reused across many different pages. <.. onclick=".."> needs to be repeatedly written every single time. DRY your code.

For tiny projects it often hardly matters. But again, sites are getting more and more complex. Business needs demand constant changes. Just one more analytics script, just one more Javascript based social widget, now change it all back, now keep all the versions of dependencies in sync, now rip it all out again and redesign for our 2.0 launch next week. Do it all with 10 other people in parallel without nuking each others code on every build or needing long sessions of resolving git-merge conflicts. In such an environment, every little bit of decoupling and indirection and flexibility helps.

Since you mention Angular:

Angular avoids some of these issues by employing a completely different template parsing model. When you write onclick=foo, then you need to bind to a global function name. However, when Angular does ng-click=foo, foo is a locally scoped entity in an ng-scope. It's not a global name. Angular separates between a controller and a view, where the controller essentially exposes a specific API on the $scope object which the view can use; both the controller and the view are still interchangeable as long as the specified API contract is kept (meaning as long as the $scope object keeps the same attributes).

All ng directives are evaluated against a custom scoping and evaluation engine which does not have much to do with Javascript's default modus operandi.

deceze
  • 510,633
  • 85
  • 743
  • 889
  • I realize that it is broad topic and I wasn't looking for one answer, I wanted to hear other opinions and actually see some reasons why I shouldn't use the HTML attributes. You mentioned good points in the answer, so thank you for taking time and explaining! – leopik Jun 08 '15 at 14:54