0

I'm sorry, if my question is very simple, but can't understand it. As I understand it's good practice to attach links/buttons listeners via js but not html. I mean this is bad practice:

<a href="javascript:" onclick="temp();">

And this is good

<a href="javascript:" id="mylink">
in js code
$("#mylink").on...

As I understand the main idea is that html page mustn't contain any javascript code, and all js code must be in js file. Ok, I like this idea.

The problem I see, implementing such approach is when to attach such listeners.

All I found - is that listeners can be attach only when document is ready (otherwise we can't add listeners to elements that are not ready).

However, if I have a page that shows for example 100 rows from database, then its loading is not fast (at comparison to short pages).

And if we wait until document is ready, the user can't work with site.

Please correct me if I'm wrong or say how can we make it to work with long pages without such things as timeouts.

PHCJS
  • 445
  • 4
  • 19
  • `As I understand the main idea is that html page mustn't contain any javascript code` Why not? The html page can contain javascript. Just, if you use jQuery, you are use `$('selector').on('click', function() {});` instied onclick. And this is totally unnecessary: `href="javascript:"` use `href="#"` and `e.prevenDefault();` on that element. – vaso123 Dec 10 '14 at 09:40
  • 1
    to wait until document is ready: $().ready(function() {...YOUR CODE...} ); in case you add everything at the end of the file, in most cases you don't even need to wait on ready. – axel.michel Dec 10 '14 at 09:40
  • Here is SO question on your problem http://stackoverflow.com/questions/19355791/jquery-ready-equalent-event-listener-on-elements – zavg Dec 10 '14 at 09:40
  • @lolka_bolka You know to define link in html and immediately after this link insert makes html page worse. At least it seems to me. I mean code is becoming dirtier. –  Dec 10 '14 at 09:48
  • @PashaTurok Sometimes, when I need a script only for one page, I am using inline scripts, because I do not want to load that for all the pages, because of performance. Why is that dirty? – vaso123 Dec 10 '14 at 09:51
  • @lolka_bolka What about "javascript:" instead of # - it helps to avoid using something like e.prevetDefault and return false. –  Dec 10 '14 at 09:51
  • @lolka_bolka It's about keeping HTML/CSS/JS all separate. It makes your HTML tidier to remove inline event-handles. It's easy to mess up with inline handlers. For example the properties of the document object are also in the scope of inline handlers, which can lead to some surprising bugs: See this: http://jsfiddle.net/MSLWL/ - throws an error, even if method is defined. Why? because document already has the property 'plugins'. – axel.michel Dec 10 '14 at 09:58

3 Answers3

2

You can use delegated events: Instead of waiting for an element to be created/ready and then attaching an event handler, the handler is attached to a parent element, that exists early, such as the document object, since that always exists.

Example: Instead of

$('#mylink').on('click', function() { /* do something */ });

it's

$(document).on('click', '#mylink', function() { /* do something */ });

This is normally used when elements are added dynamically, but it works just as well "while the page loads".

EDIT: Short explanation:

Basically DOM events "bubble": When an element is clicked, then the event isn't just sent to that element, but to all of its ancestors, one by one going up the DOM tree. The event contains information which element was originally clicked and you could filter/check for the element yourself:

$(document).on('click', function(event) { 
  if( $(event.target).is('#mylink') ) {
    alert('the link was clicked');
  }
});

Using the selector as the second argument in .on() jQuery does the filtering for you, and sets this to the link instead of the document.

See also this example: http://jsfiddle.net/qhsktpcs/

RoToRa
  • 37,635
  • 12
  • 69
  • 105
  • I've linked to an article explaining it, but added a short explanation. – RoToRa Dec 10 '14 at 10:53
  • Thank you very much. Your solution seems to me very very interesting. –  Dec 10 '14 at 10:57
  • OP is not asking for dynamic elements added after page loads . – Eduard Dec 10 '14 at 11:08
  • Do you have any information about perfomance? For example if I add 100 listeners to document. How fast will it suit a necessary element? –  Dec 10 '14 at 11:08
  • @PashaTurok , consider my answer, his answer requires unnecessary code which is impossible to scale because you'll have to define all your events in one single function. – Eduard Dec 10 '14 at 11:09
  • @edduvs: No, but he wants to add handlers while the page loads on elements that don't exist yet. It's the same problem/situation as if elements were added later by the script. – RoToRa Dec 10 '14 at 11:10
  • @edduvs Huh? Nothing in my solution requires "all events in one function". – RoToRa Dec 10 '14 at 11:12
  • @RoToRa , OP said "However, if I have a page that shows for example 100 rows from database, then its loading is not fast (at comparison to short pages)." No webserver will render a page before it's done with the server-side processing. That's all he asked for. – Eduard Dec 10 '14 at 11:13
  • @PashaTurok You should't have 100 event handlers in any case, even if not using my solution. You'll need to show a more complete example of what you are doing. Consider opening a new question for that. – RoToRa Dec 10 '14 at 11:14
  • @RoToRa he didn't say anything about 100 event handlers, you misread it, he just said 100 rows from the database. :) – Eduard Dec 10 '14 at 11:15
  • @edduvs Well it depends on the server-side script, but you can write it, so that it starts sending out data while still processing, and the browser will render it as it comes. – RoToRa Dec 10 '14 at 11:18
  • @edduvs He wrote: "if I add 100 listeners to document". – RoToRa Dec 10 '14 at 11:19
  • @RoToRa I think we all know he's referring to a standard web serrver with php. Otherwise, I think he'd state the obvious. – Eduard Dec 10 '14 at 11:19
  • @edduvs Yes, and what I described is the default for PHP. Try a simple page with `for ($i = 0; $i < 10; $i++) { echo "$i\n"; sleep(5); }` and watch the browser render one line every five seconds. – RoToRa Dec 10 '14 at 11:22
  • I think you are ritgh. Could you be so kind to comment edduvs' answer? –  Dec 10 '14 at 11:27
  • @RoToRa , who is ever going to fetch `100 rows from DB` for example using for, and sleep ? Really. He's speaking about `` elements that need a `click` bind on page ready. – Eduard Dec 10 '14 at 11:29
  • @RoToRa http://jsfiddle.net/6ctd2hqs/1/ , there's a correct solution and a good solution. Your's is good, but not the smartest. I know you know what I mean and the user only asked to be able to bind events after page is ready (even if it needs 10 seconds to load). – Eduard Dec 10 '14 at 11:34
  • @edduvs The `sleep` was just an example, to show that the server delivers the data before finishing. When sending out 100 Table lines, it will send out the first lines and the browser will be able to render them while the server is still working on the rest. And that's where my solution comes in: Using `ready` the user will have to wait until the whole page is loaded before the JS is active. With my solution the event handlers will already work, when only part of the page is loaded and visible. – RoToRa Dec 10 '14 at 12:13
0

Make sure to add all your JS code in this block :

$( function( ) { 
    $( '#mylink' ).on( 'click' , function ( e ) {
         console.log ( 'link clicked' );
         e.preventDefault();
    });
    // the rest of your code ... 
});

Otherwise, it will try to register click events on elements that are not yet created (since the DOM is not ready yet) and it will wait untill everything's ready ( even if you have ~100 rows to be pulled from the DB like you said )

** EDIT ** Example here : http://jsfiddle.net/6ctd2hqs/1/

Eduard
  • 3,395
  • 8
  • 37
  • 62
  • Could you explain $(function()) - what does it mean? –  Dec 10 '14 at 11:13
  • @PashaTurok , wrapping your code inside the `$( )` means that you're passing a function to `$` which is actually jQuery as you're already using it. `$('#myLink')` for example passes the element selector `#myLink` to jQuery which returns some methods `.on('click')` for example. It's the same as above, you're passing a function for jQuery to run when it initializes. After jQuery initalizes ( which it automatically does after page finishes loading ) it executes the function you just written there, which is the code above. Same as `$.ready( function() { $('#myLink').on('click', function(){} )})`. – Eduard Dec 10 '14 at 11:21
  • Then you solution is not good for me as it makes the user wait until the page is ready. By other words - he can' click the link until document is completely loaded. –  Dec 10 '14 at 11:23
  • @PashaTurok , wrapping the code inside that function, will run ONLY after the page is loaded and all the elements are ready. There's no way it doesn't do that. I'll even do a demo to show you. Even parsing 100 rows from DB, works. Try it out and see I'm right. – Eduard Dec 10 '14 at 11:24
  • @PashaTurok check this out, and see what I mean, the right solution: http://jsfiddle.net/6ctd2hqs/1/ – Eduard Dec 10 '14 at 11:32
-2

You can test if your page is ready, and then start attaching listeners. E.g. for jQuery:

$.ready(function() {
  // listeners here
})