If you're seeing this message, it means we're having trouble loading external resources on our website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Main content

A Button object type

One of the best ways to make code reusable and powerful is to use object-oriented programming, especially for making UI controls like buttons. In object-oriented programming, we think of our program world in terms of abstract object types that have particular behavior, and then we create specific instances of those object types with particular parameters. If you don't remember how to do it in JavaScript, review it here.
To use OOP to make buttons, we'll need to define a Button object type and then define methods on it, like to draw it and handle mouse clicks. We'd like to be able to write code that looks like this:
var btn1 = new Button(...);
btn1.draw();

mouseClicked = function() {
  if (btn1.isMouseInside()) {
     println("Whoah, you clicked me!");
  }
}
Let's contrast that to the code we wrote in the last article:
var btn1 = {...};
drawButton(btn1);

mouseClicked = function() {
  if (isMouseInside(btn1)) {
     println("Whoah, you clicked me!");
  }
}
It's very similar, isn't it? But there's a big difference -- all the functions are defined on the Button object type, they actually belong to the buttons. There's a tighter coupling of properties and behavior, and that tends to lead to cleaner and more reusable code.
To define the Button object type, we need to start with the constructor: the special function that takes in configuration parameters and sets the initial properties of the object instance.
As a first attempt, here's a constructor that takes in x, y, width, and height:
var Button = function(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
};

var btn1 = new Button(100, 100, 150, 150);
That certainly works, but I have another approach I'd like to recommend. Instead of taking in individual parameters, the constructor could take in a configuration object.
var Button = function(config) {
    this.x = config.x;
    this.y = config.y;
    this.width = config.width;
    this.height = config.height;
    this.label = config.label;
};
The advantage of the config object is that we can keep adding more parameters for the constructor to handle (like label), and it's still easy for us to understand what each parameter does when we construct a button:
var btn1 = new Button({
    x: 100, y: 100,
    width: 150, height: 50,
    label: "Please click!"});
But we can go a step farther than that. What if most buttons will be the same width or height? We shouldn't have to keep specifying the width and height parameters for every button, we should only have to specify them when necessary. We can have the constructor check if the property is actually defined in the config object, and set a default value if not. Like so:
var Button = function(config) {
    this.x = config.x || 0;
    this.y = config.y || 0;
    this.width = config.width || 150;
    this.height = config.height || 50;
    this.label = config.label || "Click";
};
Now we can just call it with a subset of the properties, because the other ones will be set to the default value:
var btn1 = new Button({x: 100, y: 100, label: "Please click!"});
All that work for a constructor, ay? But, it'll be worth it, I swear.
Now that we have the constructor squared away (buttoned away?), let's define a bit of behavior: the draw method. It'll be the same code as the drawButton function, but it will grab all the properties from this, since it's defined on the object prototype itself:
Button.prototype.draw = function() {
    fill(0, 234, 255);
    rect(this.x, this.y, this.width, this.height, 5);
    fill(0, 0, 0);
    textSize(19);
    textAlign(LEFT, TOP);
    text(this.label, this.x+10, this.y+this.height/4);
};
Once that's defined, we can call it like so:
btn1.draw();
Here's a program that uses that Button object to create 2 buttons - notice how easy it is to create and draw multiple buttons:
We skipped the hard part, however: handling clicks. We can start by defining a function on the Button prototype that will report true if the user clicked inside a particular button's bounding box. Once again, this is just like our function from before, but it grabs all the properties from this instead of a passed in object:
Button.prototype.isMouseInside = function() {
    return mouseX > this.x &&
           mouseX < (this.x + this.width) &&
           mouseY > this.y &&
           mouseY < (this.y + this.height);
};
Now we can use that from inside a mouseClicked function:
mouseClicked = function() {
    if (btn1.isMouseInside()) {
        println("You made the right choice!");
    } else if (btn2.isMouseInside()) {
        println("Yay, you picked me!");
    }
};
Try it out below, clicking each of the buttons:
But, there's something that irks me about the way we've set up that click handling. The whole point of object oriented programming is to bundle up all the behavior related to an object inside the object, and to use properties to customize behavior. But, we've left some of the behavior dangling outside the object, the printlns inside mouseClicked:
mouseClicked = function() {
    if (btn1.isMouseInside()) {
        println("You made the right choice!");
    } else if (btn2.isMouseInside()) {
        println("Yay, you picked me!");
    }
};
Those print statements should be better tied to each button somehow, like as something that we pass into the constructor. Just looking at it the way it is now, we could decide to pass a message into the constructor config, and define a handleMouseClick function to print it out:
var Button = function(config) {
    ...
    this.message = config.message || "Clicked!";
};

Button.prototype.handleMouseClick = function() {
    if (this.isMouseInside()) {
         println(this.message);
    }
};

var btn1 = new Button({
    x: 100,
    y: 100,
    label: "Please click!",
    message: "You made the right choice!"
});

mouseClicked = function() {
   btn1.handleMouseClick();
};
That's much nicer, since now everything associated with the each button's particular behavior is wrapped up in the constructor. But it's also overly simple. What if we wanted to do something besides printing a message, like draw a few shapes or change scenes, something that would take a few lines of code? In that case, we'd want to provide the constructor with more than just a string-- we actually want to provide it with a bunch of code. How can we pass around a bunch of code?
...With a function! In JavaScript (but not all languages), we can pass functions as parameters to functions. That's useful in many situations, but particularly useful when defining behavior for UI controls like buttons. We can tell the button, "hey, here's this function, it's a bunch of code that I want you to call when the user clicks the button." We refer to those functions as "callback" functions because they won't be called immediately, they'll be "called back" at an appropriate time later.
We can start by passing an onClick parameter that's a function:
var btn1 = new Button({
    x: 100,
    y: 100,
    label: "Please click!",
    onClick: function() {
       text("You made the right choice!", 100, 300);
    }
});
We then have to make sure our constructor sets an onClick property according to what's passed in. For the default, in case there's no onClick passed in, we'll just create a "no-op" function -- a function that does "no operations" at all. It's just there so that we can call it and not experience an error:
var Button = function(config) {
    // ...
    this.onClick = config.onClick || function() {};
};
Finally, we need to actually call back the callback function once the user clicks the button. That's actually pretty simple- we can just call it by writing the property name we saved it into and following that with empty parentheses:
Button.prototype.handleMouseClick = function() {
    if (this.isMouseInside()) {
        this.onClick();
    }
};
And now we're done - we have a Button object that we can easily create new buttons out of, making each button look different and responding differently to click events. Click around on the example below, and see what happens when you change button parameters:
Now that you have that as a template, you could customize your buttons in other ways, like different colors, or have them respond to other events, like mouseover. Try it out in your programs!

Want to join the conversation?

  • leaf red style avatar for user drawingboyrp
    Why are there || in the code above? What do they mean? I've never seen them before except for absolute value things (|#|), but I don't think that's what this is...
    (82 votes)
    Default Khan Academy avatar avatar for user
  • hopper jumping style avatar for user Homeschool Fiddler
    What does the "config." mean?

    Also, every time I go away from this page, my progress disappears. All the circles that have been filled in green in the Buttons section turn grey again.
    (58 votes)
    Default Khan Academy avatar avatar for user
    • spunky sam blue style avatar for user Travis KIdwell
      var Buttons = function(confg)
      Whenever you put anything inside of a functions parenthesis, ( ), it means you are declaring a variable and that you plan to pass something into this function. Later on when you declare the new button, the command is var btn1 = new Buttons( { .... } ) ;
      Notice the squiggly braces { }. Squiggly braces are only used to declare objects. So, what this is doing is you are creating a new object inside of the new Buttons( ) function, thus passing the object to the Buttons Constructor Function which becomes the variable config.
      (63 votes)
  • blobby green style avatar for user Colin Razevich
    Hello, I keep having a problem with the rabbit racer challenge.
    I run this code
    var btn1 = new Button({
    x : 350,
    y : 350,
    width : this.width,
    height : this.height,
    color : color(28, 23, 122),
    label : ("Hop")
    onClick: function(){
    rabbits[3].hop;
    }
    });

    But it keeps saying I have a syntax error.... I have no idea what that means.
    Any help is welcome
    (28 votes)
    Default Khan Academy avatar avatar for user
  • male robot hal style avatar for user cristian paun
    What does " UI controls " mean?
    (25 votes)
    Default Khan Academy avatar avatar for user
  • duskpin ultimate style avatar for user Tamara
    Help! I am doing the challenge and I am stuck at the first step
    //Draw the button
    var btn1 = new Button({
    x : 350,
    y : 350
    });

    draw = function() {
    btn1.draw; - my line of difficulty
    };

    I tried putting different things intstead of draw next to btn1, but the error buddy stays where he is, as firm a rock boulder. Could you please help me out?
    (10 votes)
    Default Khan Academy avatar avatar for user
  • piceratops ultimate style avatar for user Cristian Diac
    Why do we have to use isMouseInside() function in the following code?

    var btn1 = {...};
    drawButton(btn1);
    mouseClicked = function() {
    if (isMouseInside(btn1)) {
    println("Whoah, you clicked me!");
    }
    }


    It would not be easier this way?

    var btn1 = {...};
    drawButton(btn1);
    mouseClicked = function() {
    println("Whoah, you clicked me!");
    }
    (2 votes)
    Default Khan Academy avatar avatar for user
  • male robot hal style avatar for user ali mohsen
    What is the difference between this:
    var Button = function ( ) {  //object oriented
    this.onClick = function ( ) { rect(... };
    };

    and this:
    var Button = function ( ) {  //object oriented

    };
    Button.prototype.onClick = function ( ) { rect(... };
    (12 votes)
    Default Khan Academy avatar avatar for user
    • aqualine ultimate style avatar for user Nobody Chen
      In the first bunch of codes, "onClick" is a property of every object under "Button" (and can be different with each "Button" object if u set it as a parameter of the function). When u wanna call it, u say "this.onClick();".
      In the second bunch, "onClick" is a method of the "Button" class, and have to be the same with all the "Button" objects, which might make all buttons the same function. When u wanna call it, u say "btn.onClick();".
      (4 votes)
  • hopper cool style avatar for user Sbayley13
    Ok, step 1 of the challenge Rabbit Racer is really annoying and I can't figure it out. I put this at the bottom of the draw function:
    var btn1 = new Button({
    x: 350,
    y: 300
    });
    And then I'm supposed to do ANOTHER draw function according the hint where I call btn1.------
    Btw Im not sure if Im supposed to put btnt.draw(); there or not...But I've tried putting everything OUTSIDE of the other draw function and that doesn't work either...I thought your not allowed to have 2 draw functions? Please help!
    (4 votes)
    Default Khan Academy avatar avatar for user
  • old spice man green style avatar for user Gabriel
    Apologies if this is covered in a module further along, but in the "Object-oriented Button (with Click Handling)" program (last one on the "A Button object type" page), would it not be better to add something like:
    this.onClick = function() {
    text(config.clickText, this.x, this.y+this.height);
    to the button constructor (I did at line 8) so that way when creating a new button, rather than having to include the onClick function there with each, you can just use the text you want as "clickText" and have button creation be shorter and less repetitive?
    (4 votes)
    Default Khan Academy avatar avatar for user
    • old spice man green style avatar for user Bob Lyon
      What you suggest is true. However, what if I wanted one button to show a text string and the other to show an image, and a third button to show an explosion? As coded, the button is generic, leaving the user supplied onClick to handle the specifics policies that the application demands.
      (4 votes)
  • stelly blue style avatar for user Shreeya1202
    I need help with the last step of Challenge : Rabbit Racer
    (6 votes)
    Default Khan Academy avatar avatar for user
    • aqualine ultimate style avatar for user AD Baker
      In Step 3 of the Rabbit Racer challenge, we are asked to
         Change the onClick method for your button, so that it 
      calls a method on the player's rabbit (the last rabbit
      in the
      rabbits array) to make it hop forward.
      The HINT for this step is
          var btn1 = new Button({
      x: ___,
      y: ___,
      width: ___,
      height: ___,
      color: color(_______),
      label: ___,
      onClick: function() {
      rabbits[__].(___);
      }
      });
      In the starter code, the code for btn1 is on line 115.

      You should have completed most of the button in Steps 1 and 2. In Step 3, we should be just looking at the onClick method. We want to apply this method to the player's rabbit, which we know from the instruction is the last rabbit in the rabbits array.

      On line 110, we have the code that creates the Rabbit objects and adds them to the rabbits array. Look at this code to determine which index to use inside the onClick method.

      Take a look at the code for the Rabbit object (lines 51 - 102). In this code, find a method you can use to make the rabbit hop forward.
      (2 votes)