简体   繁体   中英

How to have custom object listen for child custom object's events?

I am trying to create a couple of custom objects in JavaScript and I am unsure if I am going about things the proper way.

I have two objects: 'UserInterface' and 'VolumeSlider'. Up until a moment ago these two objects were independent, but coupled through methods. I decided that UserInterface should instead 'have' a VolumeSlider.

So:

UserInterface = {
    _volumeSlider: null,

    initialize: function () {
        this._volumeSlider = VolumeSlider; //TODO: Should this be a new volumeSlider();
        this._volumeSlider.initialize();

        //I want to setup a listener for VolumeSlider's change-volume events here.
    }
}

VolumeSlider = {
    _volumeSlider: null,

    initialize: function () {
        this._volumeSlider = $('#VolumeSlider');
        var self = this;
        this._volumeSlider.slider({
            orientation: 'horizontal',
            max: 100,
            min: 0,
            value: 0,
            slide: function (e, ui) {
                self.passVolumeToPlayer(ui.value);
            },
            change: function (e, ui) {
                self.passVolumeToPlayer(ui.value);
            }
        });
    },

    passVolumeToPlayer: function (volume) {
        if (volume != 0)
            UserInterface.updateMuteButton(volume);
        else
            UserInterface.updateMuteButton('Muted');
    }
}

Question is two-fold:

  • Is this an alright way to be creating my objects and setting up children?
  • I would like UserInterface to be able to respond to VolumeSlider._volumeSlider's change and slide events instead of having to be explicitly told. How can I go about accomplishing this?

Is this an alright way to be creating my objects and setting up children?

If it works, it's "alright" :)
But in all seriousness, there are practically an infinite number of ways to set something like that up. Depends very much on your needs.

I will say, though, that your code will be unwieldy if, for instance, there needs to be more than 1 slider.


I would like UserInterface to be able to respond to VolumeSlider._volumeSlider's change and slide events instead of having to be explicitly told. How can I go about accomplishing this?

I'd probably just skip the VolumeSlider object. It's not a constructor ("class"), so it's single-use and hard-coded to the #VolumeSlider element. Also, from your code, it doesn't really do all that much. Seems to just be a middle-man.

Here's an alternative:

UserInterface = {
  initialize: function () {
    // Internal function to create a horizontal 0-100 slider
    // Takes a selector, and a function that will receive the
    // value of the slider
    function buildSlider(selector, callback) {
      var element = $(selector);
      var handler = function (event, ui) { callback(ui.value); };
      element.slider({
        orientation: 'horizontal',
        max:   100,
        min:   0,
        value: 0,
        slide:  handler,
        change: handler
      });
      return element;
    }

    // We probably want to bind the scope of setVolume, so use $.proxy
    this.volumeSlider = buildSlider("#VolumeSlider", $.proxy(this.setVolume, this));

    // Want more sliders? Just add them
    // this.speedSlider = buildSlider("#SpeedSlider", $.proxy(this.setSpeed, this));
    // this.balanceSlider = buildSlider("#BalanceSlider", $.proxy(this.setBalance, this)); 
    // etc...
  },

  setVolume: function (value) {
    if( value != 0 ) {
      // not muted
    } else {
      // muted
    }
  }
}

With this you could conceivably create X number of sliders for any purpose you need. And there's no separate single-use VolumeSlider object; everything's handled in the UserInterface object.

Of course, that in itself might not be the best idea, but for this simple purpose it's OK. However, you might want to extract the buildSlider into a more general UIElements object. It could have methods like buildSlider , buildButton , etc.

Alternatively, you could go the "Unobtrusive JavaScript"-way and have special class names and data- attributes, that would declare how and where an element fits in the UI. For instance

<div id="volumeSlider" class="ui-element" data-role="slider" data-action="setVolume">...

Could be found along with all the other UI elements using $(".ui-element").each ... after which its attributes could be parsed to mean that it should be made into a slider called "volumeSlider", and its slide/change events should call setVolume ... Et cetera.

Again, many, many ways of going about it.

I don't think your original structure would become unwieldy at all. JavaScript's built-in data structures will help in this scenario.

If you need more than one volume slider, simply change this bit _volumeSlider: null, to _volumeSliders: [],

If you need sliders by reference, initialize _volumeSliders to a dict . Then refer to them by _volumeSliders[selector] , and you've got your object.

Instead of a function to pass values up, send a reference to the parent instance down. Then you can write, parent.volume = value .

As a rule of thumb, I rigidly adhere to a solid, logical, hierarchical structure. Never alter your architecture, simply create scope references. In this case, a reference to the parent scope. I wouldn't use the first answerer's method because I might have to grep around to find the source of the objects being created.

The first answerer has some good ideas. You may notice certain patterns that repeat themselves, eg setting a jQuery element reference. I typically wait until I notice these kinds of repeated patterns, and then DRY it up with an abstraction, eg a base class, like Flambino suggested, eg UIElement .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM