1

I want to add an unobtrusive event handler to a button using javascript with prototype. I'm written a very simple HTML example below where there are two buttons. The first button has the onclick added to it within the HTML. The second button has the id "googleButton" that should have an event handler attached to it in my javascript. I have tried many different forms of attaching the event handler and all of them are throwing errors. I can get this kind of thing to work very easily in jQuery, maybe I'm missing something about the prototype syntax?

Here's the HTML:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Assignment 10 - Part 1</title>
        <script src="./js/prototype.js" type="text/javascript"></script>
        <script src="./js/scriptaculous/scriptaculous.js?load=effects,controls" type="text/javascript"></script>
        <script src="./js/assignment10p1.js" type="text/javascript"></script>
        <link rel="stylesheet" type="text/css" href="css/style.css" />
    </head>
    <body>
        <fieldset>
            <legend>City Search</legend>
            <label for="cityName1">Enter a city name: </label>
            <input type="text" id="cityName1">
            <input type="button" value="Google City" onclick='googleSearch("cityName1")'>
            <input id="googleButton" type="button" value="Google City">
            <div id="cityAutoComplete" class="autocomplete"></div>
        </fieldset>   
    </body>
</html>

Here's the javascript:

var cities = ["Aachen", "Aalborg", "Aarhus", "Abbeville", "Abbot", "Abbotsford",
              "Aberdeen", "Abernathy", "Acadia", "Acampo", "Acra", "Adams", "Addison",
              "Addy", "Adrian", "Agate", "Agua Caliente", "Aiken", "Ainsworth",
              "Akron", "Alamo", "Alba", "Albany", "Alberta", "Alden", "Alta", "Amboy",
              "Amherst", "Anchorage", "Anderson", "Andover", "Appleton", "Arcadia", 
              "Ardmore", "Argyle", "Arlington", "Arthur", "Ashburn", "Ashland", 
              "Ashton", "Athens", "Atlanta", "Auburn", "Aurora", "Austin", "Aztec",
              "Bagwell", "Bailey", "Bainbridge Island", "Baker", "Bakersfield", 
              "Baldwin", "Ballard", "Baltic", "Bangkok", "Banner", "Barcelona",
              "Barlow", "Barnesville", "Barnum", "Barrington", "Bartlett", "Barton",
              "Batesville", "Bath", "Battle Creek", "Baxter", "Bay City", "Beach", 
              "Bear Creek", "Beaumont", "Beaver", "Bedford", "Belcourt", "Belfast",
              "Belgium", "Bellingham", "Bellevue", "Belmont", "Belvidere", "Benson",
              "Berkley", "Berlin", "Bethany", "Beverly", "Big Bear", "Biggs", 
              "Billings", "Bingham", "Birmingham", "Bishop", "Blackfoot", "Blain",
              "Bloomfield", "Bloomington", "Blue Springs", "Boggs", 
              "Boiling Springs", "Boise", "Bolton", "Boston", "Bradford", "Bremerton",
              "Brooklyn", "Brooks", "Brunswick", "Brussels", "Buffalo", "Burbank",
              "Burlington", "Butte", "Byron"];

function googleSearch(id) {
    var city = $(id).value;
    window.location.href="http://www.google.com/search?q=" + city;
}

document.observe("dom:loaded", function() {
    new Autocompleter.Local("cityName1", "cityAutoComplete", cities);
});

// This should add the event handler
$('googleButton').observe('click', googleSearch('cityName1'));

Here's the error firebug gives:

TypeError: $(...) is null
$('googleButton').observe('click', googleSearch('cityName1'));

Sorry this is a repost, I deleted the previous question thinking I'd found the problem, but it persists. Others had commented that I needed to do it like this:

$('#googleButton').observe('click', googleSearch('cityName1'));

But that's jQuery syntax, not prototype. Another person said it should be working and that's all I heard. Other than the libraries loaded above this is literally the full example.

1 Answer 1

5

There are two issues:

First, you're hooking up the handler wrong. This code:

$('googleButton').observe('click', googleSearch('cityName1'));

calls googleSearch immediately, passing in 'cityName1', and passes the return value into observe, exactly the way foo(bar()) calls bar and passes its return value into foo. That should be:

$('googleButton').observe('click', function() {
    googleSearch('cityName1');
});

...or

$('googleButton').observe('click', googleSearch.curry('cityName1'));

...which basically does the above, but using Prototype's Function#curry.

With either of those, we pass a function into observe that, when called, will call googleSearch passing in 'cityName1'.

Second, scripts run immediately when they're encountered in the HTML (unless you use the defer or async attributes, which modify the behavior). Since the script is above the markup for the element in the HTML, the element doesn't exist yet when you're trying to get access to it.

If you move that code into your dom:loaded callback above it, it should work.

Alternately, you can just move your scripts to the end of the HTML, just before the closing </body> tag, as is usually the recommendation. Then you can do away with dom:loaded; all of the elements defined by the markup above the script exist by the time it runs.

Sign up to request clarification or add additional context in comments.

6 Comments

I suspected this was the problem too and had tried that. Adding it to the dom:loaded function causes it to immediately fire googleSearch the second the page loads rather than waiting for a click event. While I know adding it to the end will work, I'm trying to learn how to do this in a prototype-esque way (as doing it in jQuery is quite easy).
@sage88: There was a second problem, which I've added to the beginning of the answer. Neither of these were down to differences between jQuery and Prototype. If you'd had $('#googleButton').on('click', googleSearch('cityName1')); (e.g., using jQuery) in that same place, the symptom would have been a bit different (triggering the search right away), but both problems were there. Best,
Thank you, that solves this completely. I'm just now realizing how similar prototype and jQuery are... I would have done the above had I been using jQuery. Unfortunately, I was trying to work off the prototype api documentation and they just do what I'd done above (and I assumed wrongly that prototype just did it differently), which is non-functional. Thanks for the curry example that's a very nice use.
@sage88: You're welcome. Where do you think you see that in the Prototype documentation? I used to be pretty familiar with it (and it's not like they've updated it recently), I don't recall seeing errors like that.
prototypejs.org/doc/latest/dom/Event/observe it's not so much an error as an omission of how you'd actually want to do it in practice inside of dom:loaded and that omission hides the fact that giving it to dom:loaded the way they have it will fire it immediately.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.