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?
$(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:
$
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. $
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.