1

This works when the script is embedded. Why does it only work for two clicks when the code is in an external script?

Embedded case:

<html>    
<head>
  <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
</head>    
<body>
  <h1 id="title" onclick="sayGoodbye();"> Hello </h1>
</body>    
<script type="text/javascript">
  function sayGoodbye() {
    console.log("goodbye");
    $("#title").html("Goodbye");
    $("#title").click(function() {
      $("#title").html("Hello");
      $("#title").off("click");
    });
  };
</script>    
</html>

External file tested as follows:

<body>
  <h1 id="title"> Hello </h1>
</body>

External Javascript file:

function sayGoodbye() {
  console.log('goodbye');
  $('#title').html('Goodbye');
  $('#title').click(function() {
    $('#title').html('Hello');
    $('#title').off('click');
  });
};

$(function(){
   $('#title').on('click', sayGoodbye);
});
Paul
  • 348
  • 2
  • 16
  • See http://stackoverflow.com/questions/2037203/why-is-my-function-call-that-should-be-scheduled-by-settimeout-executed-immediat and related questions for all the details desired. – user2864740 Sep 26 '15 at 02:28
  • Please do *not* use stack-overflow as a 'live debugging' service. One question/problem only, after collecting an adequate problem description. Post a new question - after more due searching and problem analysis - for a different problem. – user2864740 Sep 26 '15 at 04:53

2 Answers2

1

The problem has nothing to do with the file being external or embedded.

You've changed your code, and that's why it stopped working. You could keep your HTML code identical to the original one <h1 id="title" onclick="sayGoodbye();"> Hello </h1>.

Since you've changed, that's the reason it's not working anymore.

When adding event listeners via Javascript's addEventListener or jQuery's on, you must always pass a function reference, and never a function execution. If you use the (), the function will be called immediately, and the returned value of it will be passed to the other function you're calling.

For example, if I do that:

function num() {
  return 5;
}

console.log(num);   // it will log 'function num()'
console.log(num()); // it will log 5

Since your sayGoodbye function returns nothing, the value passed to jQuery's on will be undefined.

function num() {
   var y = 5;
   y = y + 10;
}

console.log(num);   // it will log 'function num() '
console.log(num()); // it will log 'undefined'

When you put in HTML onclick="sayGoodbye();", you're in fact saying:

Element.onclick = function() {
  sayGoodbye();
}

It automatically wrap your code into an anonymous function, and pass it as a reference to the event.


Tl;dr

Your code isn't working because you're using:

$('#title').on('click', sayGoodbye());

Which evaluates/calls the function immediately, because the parenthesis are present.

Instead, you should supply a callback function by name:

$('#title').on('click', sayGoodbye);

Or as an anonymous function:

$('#title').on('click', function() {
  sayGoodbye();
});

Update

After you changing the question, it seems to be another simple problem.

Where are you defining your <script src="externalscript.js"></script>?

You should be placing it at the same place where your embedded one was. If you've put it into the head tag, it will be evaluated even before your h1#test is loaded, and then, it won't find the Element, and don't add the event listener to it.

But if you really want/need to put it inside the head tag, then, you must wrap your code into an load event listener. If using vanilla Javascript, it would be:

document.addEventListener('DOMContentLoaded', function() {
  // your code
});

But since you're using jQuery, you can use $(document).ready:

$(document).ready(function() {
  // your code
});

And there is a shorthand for it, that's just wrap into $();:

$(function() {
  // your code
});

Note: I've just realised that your biggest problem here is that you don't understand the difference between doing <h1 id="title" onclick="sayGoodbye();"> Hello </h1>, and adding your event handler through jQuery.

Your first example is working the way you expected, because when you do:

$("#title").html("Goodbye");
$("#title").click(function() {
  $("#title").html("Hello");
  $("#title").off("click");
});

You're saying:

  1. Change the innerHTML property of the element with id title to 'Goodbye'
  2. Add a click event handler that:

    2.1 Change the innerHTML property of the element with id title to 'Hello'

    2.2 Remove all the jQuery event handlers for the event click

So, what is going to happen:

  1. When you click the first time

    1.1 Your h1 will change to 'Goodbye'

    1.2 And there will be a new click event handler for it

  2. When you click the second time

    2.1 It will fire the same event again firstly, so your h1 will change to 'Goodbye'

    2.2 There will be a new click event handler for it

    2.3 The previously event handler added on 1.2 will be fired

    2.4 Then, your h1 will change back to Hello

    2.5 And you remove all the jQuery click event handlers of it

So, when you click the third time, it will be like it was when the page was loaded, because the event handler placed into the HTML code will still remains, but all the jQuery event handlers that were attached to the h1 were removed.

That said, 3rd will be the same as 1st, 4th will be the same as 2nd, and so on.


When you stop attaching the event handler into your HTML code, and start attaching it through jQuery, your code stops working because:

  1. When you click the first time

    1.1 Your h1 will change to 'Goodbye'

    1.2 And there will be a new click event handler for it

  2. When you click the second time

    2.1 It will fire the same event again firstly, so your h1 will change to 'Goodbye'

    2.2 There will be a new click event handler for it

    2.3 The previously event handler added on 1.2 will be fired

    2.4 Then, your h1 will change back to Hello

    2.5 And you remove all the click event handlers of it

As you can see, pretty the same as the example above. But there is one big difference: When 2.5 happens, you don't have any click event handlers attached to the h1 anymore, since you've removed all of them there.

That happens, because when you pass only one parameter to the jQuery's off method, you're saying "Remove all the jQuery's event handlers for that type of event" (in that case, click). So, it will remove both the event handler attached to sayGoodBye and the one that you've created with the anonymous function.

In the first example, it doesn't happen, because jQuery doesn't know about the event you've attached via HTML code, so it never removes it, and your code works the way you expect.

So, if you want to work with jQuery only, your code should be something like:

$(function(){
  function sayGoodbye() {
    $('#title')
      .html('Goodbye')
      .off('click', sayGoodbye)
      .on('click', sayHello);
  }

  function sayHello() {
    $('#title')
      .html('Hello')
      .off('click', sayHello)
      .on('click', sayGoodbye);
  }

  $('#title').on('click', sayGoodbye);
});
Buzinas
  • 11,597
  • 2
  • 36
  • 58
  • $('#title').on('click',sayGoodbye); Sorry, but it did not work. – Paul Sep 26 '15 at 04:01
  • @Paul The chances are you are not waiting for the DOM to load. See `jQuery.ready` (search for `[jquery] ready` in SO, or look at the jQuery documentation to find out what this means). Also, please don't change questions such that the *original problem is lost*. Accept that it is a problem and create a new question.. SO should *not* be used as a 'live debugging' service. – user2864740 Sep 26 '15 at 04:50
  • @Paul I've updated my answer. Please, take some time and try to understand it, since you don't understand what you're doing there. One little tip: before using jQuery, try to learn Javascript better, because it will help you to understand what is going on behind the scenes. – Buzinas Sep 26 '15 at 13:12
  • @Buzinas Script load is not in the head. It's is at the bottom of the body. Don't make false assumptions and try your answer before posting. – Paul Sep 26 '15 at 14:49
  • @Paul lol, have you tried to read my answer to the end? That's really funny, I take my time trying to help you understand the lack of knowledge you have, and I get back a punch... You're the best, congrats! – Buzinas Sep 26 '15 at 20:44
  • This link shows the ideal way to answer a question. It is concise, without sarcasm or judgement. [link] (http://stackoverflow.com/questions/32798540/why-does-jquery-on-off-method-behave-differently-from-external-script) – Paul Sep 27 '15 at 17:07
0

The syntax for passing a function reference is incorrect.

$('#title').on('click',sayGoodbye());

Will call the function immediately due to the ().

Change to:

$('#title').on('click',sayGoodbye);
charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • There are no references in JavaScript (unless talking about the Reference Specification Type, but..). It's much tidier to just refer to it as a function-object. Then there is no confusion with call by reference (which is *not* supported) or implementation details of *other* languages. – user2864740 Sep 26 '15 at 02:23
  • 1
    @user2864740 that is absurd – charlietfl Sep 26 '15 at 02:24
  • @charlietfl will a function be called even without `()` if it will then this will work right?im just a noob who wants clarification. – guradio Sep 26 '15 at 02:27
  • @Pekka yes, you are passing in the function object, not it's result when it gets called – charlietfl Sep 26 '15 at 02:30
  • @Pekka A function can *only* be called directly with `f()` (or indirectly through a call/apply, etc). The expression `sayGoodbye` simply *evaluates to* the function object, without invoking it. – user2864740 Sep 26 '15 at 02:30
  • OK, I changed it to $('#title').on('click',sayGoodbye); Still not working. – Paul Sep 26 '15 at 04:02
  • make sure it is wrapped in `$(function(){ /* $('#title').on... })` if script tag is in head otherwise will be looking for elements that don't exist yet – charlietfl Sep 26 '15 at 04:03
  • $(function(){$('#title').on('click', sayGoodbye);}); I tried this but still same problem. – Paul Sep 26 '15 at 04:29
  • I am using Chrome, and the developer console only showing the logs no errors. – Paul Sep 26 '15 at 04:36
  • How?I tried your fiddle link it has same problem on Firefox, and Chrome. – Paul Sep 26 '15 at 04:41
  • That's the problem I have been saying. – Paul Sep 26 '15 at 04:47
  • stops because you told it to `$("#title").off("click");` I thought you were saying it didn't work at all – charlietfl Sep 26 '15 at 04:50
  • Because `off()` doesn't manage `onclick` inline. The code doesn't make a lot of sense...not sure what your expectations are for it – charlietfl Sep 26 '15 at 04:52