简体   繁体   中英

How do I access an object via another object that contains an object which is a property of the object I want to have access to?

It was kinda hard to come up with the title for this one, hope it's understandable and makes sense.

Well , maybe, the difficulty to come up with the title reflects some unnecessary complexity of this situation - if there's a simple way to do this I'd love to know.

In this sample, there's a Car 'class', from which I can instance objects (Cars). It has a template (an SVG, which is not a car, actually, but who cares), and also public methods, used to modify the template, via jQuery.

In the carContainerClick() function, how do I access the Car instance whose template is contained by the currently clicked carContainer element?

Sample here

$(function(){

  var cars = [];

  for (var i = 0; i < 2; i++) {

    var carContainer = $('<div/>', { class: 'car-container'});
    var car = new Car();

    cars[i] = car;

    carContainer.on('click', carContainerClick);

    carContainer.append(car.getTemplate());
    $('.container').append(carContainer);
  }

  function carContainerClick() {

    // HERE - how do I access the Car instance whose template is contained by the currently clicked carContainer element?

    cars[0].changeColor();
  }
});

function Car () {
  this.template = $('<svg viewBox="0 0 301 259">    <g class="svg-group"><path class="stick-2" fill-rule="evenodd" clip-rule="evenodd" d="M74.192,27.447c2.589-2.042,4.576-3.188,6.991-5.093c0,0,1.753-1.11,0.416-2.945 c-1.13-1.546-3.242,0.014-3.242,0.014c-4.831,3.804-9.678,7.589-14.491,11.418c-2.335,1.861-4.335,4.009-7.954,3.233 c-2.136-0.458-3.892,1.798-3.913,4.021c-0.02,2.326,1.531,4.107,3.734,4.296c2.353,0.2,4.689-1.183,4.635-3.241    c-0.066-2.415,1.215-3.474,2.981-4.492c1.821-1.049,5.809-3.993,7.21-4.785C71.961,29.082,74.192,27.447,74.192,27.447z"/></g></svg>');
}

Car.prototype = {
  getTemplate: function() {
    return this.template;
  },
  changeColor: function() {
    console.log('changeColor');
    $('.svg-group', this.template).find("path, polygon, circle").attr("fill", "#aff");
  }
};

update

i made a little test with the solutions provided here and turns out it makes almost no difference in performance .. but i like the IIFE one for its simplicity. Sample

You can use an inline function as your event handler that passes car as a parameter... however, because the car declaration is hoisted out of your loop (specifically to the top of the current function), you need to ensure that the event handler captures the right Car instance, and not the last one to pass through the loop.

You can create a closure by using a self invoking function expression that creates a new scope for car that exists within the loop and allows the right car instance to be captured when you set up your handler.

  for (var i = 0; i < 2; i++) {
    (function(){
      var carContainer = $('<div/>', { class: 'car-container'});
      var car = new Car();
      cars[i] = car;
      carContainer.on('click', function(){
        carContainerClick(car);
      });
      carContainer.append(car.getTemplate());
      $('.container').append(carContainer);
    })();
  }

Now you can have a function that gets handed the Car instance you need.

  function carContainerClick(car) {
    car.changeColor();
  }

Use closure to pass the car instance as an argument to the carContainerClick function.

I extracted a new function, cause doing that stuff in a for loop will probably not work (always passing the last car as an argument)

for (var i = 0; i < 2; i++) {
    var carContainer = $('<div/>', { class: 'car-container'});
    var car = new Car();

    cars[i] = car;

    bindCarContainer(carContainer, car);

    carContainer.append(car.getTemplate());
    $('.container').append(carContainer);
}

function bindCarContainer(carContainer, car) {
    carContainer.on('click', function(event) {
        carContainerClick.call(this, car, event);
    });
}

function carContainerClick(car, event) {
    //...
}

Try assigning storing it in a property:

$container = $('.container');
for (var i = 0; i < 2; ++i) {
    var car = new Car(),
        carContainer = $('<div/>').addClass('car-container').prop('car', car);
    carContainer.on('click', carContainerClick);
    carContainer.append(car.getTemplate());
    $container.append(carContainer);
}

function carContainerClick() {
    this.car.changeColor();
}

var $template = $('<svg viewBox="0 0 301 259">    <g class="svg-group"><path class="stick-2" fill-rule="evenodd" clip-rule="evenodd" d="M74.192,27.447c2.589-2.042,4.576-3.188,6.991-5.093c0,0,1.753-1.11,0.416-2.945 c-1.13-1.546-3.242,0.014-3.242,0.014c-4.831,3.804-9.678,7.589-14.491,11.418c-2.335,1.861-4.335,4.009-7.954,3.233 c-2.136-0.458-3.892,1.798-3.913,4.021c-0.02,2.326,1.531,4.107,3.734,4.296c2.353,0.2,4.689-1.183,4.635-3.241    c-0.066-2.415,1.215-3.474,2.981-4.492c1.821-1.049,5.809-3.993,7.21-4.785C71.961,29.082,74.192,27.447,74.192,27.447z"/></g></svg>');

function Car () {
    this.$template = $template.clone();
}

Car.prototype = {
    getTemplate: function() {
        return this.$template;
    },
    changeColor: function() {
        console.log('changeColor');
        $('.svg-group', this.$template).find("path, polygon, circle").attr("fill", "#aff");
    }
};

Some notes:

  • Don't use $ to get elements inside a loop, it's very slow! Store the result in a variable before the loop instead
  • { class: 'car-container'} may fail on old browsers because class was reserved. Try 'class' with quotes or addClass method.
  • To remember what is a DOM element and what is a jQuery wrapper, you can use $ at the beginning of the variable name.

Use jQuery's .data() to attach a reference to the car to the template:

function Car () {
    this.template = $('<svg viewBox="0 0 301 259">    <g class="svg-group"><path class="stick-2" fill-rule="evenodd" clip-rule="evenodd" d="M74.192,27.447c2.589-2.042,4.576-3.188,6.991-5.093c0,0,1.753-1.11,0.416-2.945 c-1.13-1.546-3.242,0.014-3.242,0.014c-4.831,3.804-9.678,7.589-14.491,11.418c-2.335,1.861-4.335,4.009-7.954,3.233 c-2.136-0.458-3.892,1.798-3.913,4.021c-0.02,2.326,1.531,4.107,3.734,4.296c2.353,0.2,4.689-1.183,4.635-3.241    c-0.066-2.415,1.215-3.474,2.981-4.492c1.821-1.049,5.809-3.993,7.21-4.785C71.961,29.082,74.192,27.447,74.192,27.447z"/></g></svg>');
    this.template.data('car', this);
}

Then later you can access it from the jQuery object:

function carContainerClick() {
    var car = this.data('car');
    car.changeColor();
}

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