create jquery extension. problems with scope - javascript

I create a simple jQuery extension(it's my first).
(function($){
var MyClass = function(opt){
//..
};
//one of the methods of my extension
$.fn.myExtension = function(opt){
this._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function(){
if(this._ext){
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function(){
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
when i invoke method $('#selector').myExtensionOtherMethod();, this does not contains this._ext variable. I know that this is other scope, but i know that there is some way to access that variable in both methods.how can i do it?

This isn't really a scope issue. This is because the jQuery prototype $.fn gives you a jquery object as this. Even though you are selecting the same element each time its a new jQuery object set as the context so that property is gone. You can put that property on the DOM element and achieve the outcome you want.
(function($) {
var MyClass = function(opt) {};
//one of the methods of my extension
$.fn.myExtension = function(opt) {
this[0]._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function() {
if (this[0]._ext) {
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function() {
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
This is just a quick example above. If your selector finds more than one element you should loop though them. But I only grabbed the first index since you were selecting by ID.
Fiddle: https://jsfiddle.net/AtheistP3ace/gd1ehk0d/
As mentioned above by #charlietfl, I agree with that comment. Happy to explain why what you did didn't work but there may be better ways to achieve what you are looking for.

Related

If I create OOP JS code using prototype, how do I reference a class method from a loop?

I'll show you my code first:
function Messages(){
this.postResponseButton = '#postResponseButton';
$(document).ready(this.setEvents);
}
Messages.prototype.setEvents = function(){
$(self.postResponseButton).click(function(){
this.postResponse(); // ERROR HERE
});
}
Messages.prototype.postResponse = function(){
console.log('Post Response');
}
var messages = new Messages();
In the marked line ("ERROR HERE"), it's not recognizing the Messages.postResponse() function when I call it as this.postResponse(). I've also tried self.postResponse() without any success.
I'm sure it's a problem of scope; I'm just not sure how to refer to the actual object. Do I need to set var me = this and use that, or something?
Thanks for your time!
As you have said, the problem is that the context of the click event handler is not the same as the function in which it appears. Either bind (ES5, won't work in old browsers) the function to this:
Messages.prototype.setEvents = function(){
$(self.postResponseButton).click(function(){
this.postResponse();
}.bind(this));
}
Or save a reference to this and use that instead:
Messages.prototype.setEvents = function(){
var that = this;
$(self.postResponseButton).click(function(){
that.postResponse();
});
}
A third alternative would be to use $.proxy, which is effectively an alias for Function.prototype.bind including a fallback for old browsers:
Messages.prototype.setEvents = function(){
$(self.postResponseButton).click($.proxy(function(){
this.postResponse();
}, this));
}

javascript prototype class, this in jquery click

I made a javascript prototype class.
Inside a method I create an jquery click.
But inside this click I want to execute my build function.
When I try to execute a prototype function inside a jquery click it fails because jquery uses this for something else.
I tried some different things, but I couldnt get it working.
Game.prototype.clicks = function(){
$('.flip').click(function(){
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
$(this).find('.card').addClass('flipped');
cardsPlayed.push($(this).find('.card').attr('arrayKey'));
console.log(cardsPlayed[cardsPlayed.length - 1]);
console.log(playingCards[cardsPlayed[cardsPlayed.length - 1]][0]);
if(cardsPlayed.length == 2)// two cards played
{
if(playingCards[cardsPlayed[0]][0] == playingCards[cardsPlayed[1]][0])
{ // same cards played
console.log('zelfde kaarten');
playingCards[cardsPlayed[0]][0] = 0; //hide card one
playingCards[cardsPlayed[1]][0] = 0; //hide card two
//rebuild the playfield
this.build(); //error here
}
else
{
//differend cards
}
}
}
return false;
}).bind(this);
}
The problem is that you're trying to have this reference the clicked .flip element in $(this).find('.card') as well as the Game object in this.build(). this can't have a dual personality, so one of those references needs to change.
The simplest solution, as already suggested by Licson, is to keep a variable pointing to the Game object in the scope of the click handler. Then, just use this inside the handler for the clicked element (as usual in a jQuery handler) and use self for the Game object.
Game.prototype.clicks = function() {
// Keep a reference to the Game in the scope
var self = this;
$('.flip').click(function() {
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
// Use this to refer to the clicked element
$(this).find('.card').addClass('flipped');
// Stuff goes here...
// Use self to refer to the Game object
self.build();
}
}); // Note: no bind, we let jQuery bind this to the clicked element
};
I think you want something like this:
function class(){
var self = this;
this.build = function(){};
$('#element').click(function(){
self.build();
});
};
If I understand correctly, in modern browsers you can simply use bind:
function MyClass() {
this.foo = 'foo';
$('selector').each(function() {
alert(this.foo); //=> 'foo'
}.bind(this));
}
Otherwise just cache this in a variable, typically self and use that where necessary.

Adding a function to one jQuery/DOM element

I am authoring a plugin which instantiates a map. The map would then provide a function to move to another place on the earth.
The script makes the map just fine. However I can't "tack" the function on the element, to be used by another plugin in a callback.
Here's the approach I tried; in plugin:
(function($){
$.fn.mapDo(options){
map = new BlahMap(this.get(0));
this.moveTheMap = function(place){
map.moveItToThat(place);
}; // nope.
}
})(jQuery);
Then, in view:
$(map).mapDo();
$(otherElement).otherControl({
callback: function(place){
$(map).moveTheMap(place); // moveTheMap is not there on $(map)!
}
};
The Question
How do I add a function to the map jQuery or DOM element, if possible? If not, how can I provide that kind of functionality?
More importantly, am I going the right way here by separating the things that way? I'm a bit of a neophyte to Javascript, how are these tasks usually done while still keeping the components apart?
While that's the stab I took at it, more generally, I struggled with the concept of outputting things from a jQuery plugin while maintaining chainability. In this case, what I am trying to do is to output a callback from the plugin that will work on the called element later in the execution.
Plugins normally only add one method to the jQuery prototype, and the method calls to the plugin's instances are done with strings.
(function($) {
$.fn.mapDo = function(options) {
var args = [].slice.call(arguments, 1); //Get all the arguments starting from 2nd argument as an array
return this.each(function() {
var $this = $(this),
instance = $this.data("map-instance");
if (!instance) {
$this.data("map-instance", (instance = new BlahMap(this, options)));
}
if (typeof options == "string") {
instance[options].apply(instance, args);
}
});
};
})(jQuery);
$(elem).mapDo( "moveTheMap", place ); //This would also instantiate the plugin if it wasn't instantiated
Here's jsfiddle showing it in action:
http://jsfiddle.net/X8YA8/1/
You could store the map with .data method.
(function($){
$.fn.mapDo = funciont(options) {
this.data('map', new BlahMap(this.get(0)));
return this;
};
$.fn.moveTheMap = function(place) {
var map = this.data('map');
if (map) {
map.moveItToThat(place);
}
return this;
};
})(jQuery);

call function inside a nested jquery plugin

There are many topics related to my question and i have been through most of them, but i haven't got it right. The closest post to my question is the following:
How to call functions that are nested inside a JQuery Plugin?
Below is the jquery plugin i am using. On resize, the element sizes are recalculated. I am now trying to call the function resizeBind() from outside of the jquery plugin and it gives me error
I tried the following combinations to call the function
$.fn.splitter().resizeBind()
$.fn.splitter.resizeBind()
Any ideas, where i am getting wrong?
;(function($){
$.fn.splitter = function(args){
//Other functions ......
$(window).bind("resize", function(){
resizeBind();
});
function resizeBind(){
var top = splitter.offset().top;
var wh = $(window).height();
var ww = $(window).width();
var sh = 0; // scrollbar height
if (ww <0 && !jQuery.browser.msie )
sh = 17;
var footer = parseInt($("#footer").css("height")) || 26;
splitter.css("height", wh-top-footer-sh+"px");
$("#tabsRight").css("height", splitter.height()-30+"px");
$(".contentTabs").css("height", splitter.height()-70+"px");
}
return this.each(function() {
});
};
})(jQuery);
I had the same problem. Those answers on related posts didn't work for my case either. I solved it in a round about way using events.
The example below demonstrates calling a function that multiplies three internal data values by a given multiplier, and returns the result. To call the function, you trigger an event. The handler in turn triggers another event that contains the result. You need to set up a listener for the result event.
Here's the plugin - mostly standard jQuery plugin architecture created by an online wizard:
(function($){
$.foo = function(el, options){
// To avoid scope issues, use 'base' instead of 'this'
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data("foo", base);
base.init = function(){
base.options = $.extend({},$.foo.defaultOptions, options);
// create private data and copy in the options hash
base.private_obj = {};
base.private_obj.value1 = (base.options.opt1);
base.private_obj.value2 = (base.options.opt2);
base.private_obj.value3 = (base.options.opt3);
// make a little element to dump the results into
var ui_element = $('<p>').attr("id","my_paragraph").html(base.private_obj.value1 +" "+ base.private_obj.value2+" " +base.private_obj.value3);
base.$el.append(ui_element);
// this is the handler for the 'get_multiplied_data_please' event.
base.$el.bind('get_multiplied_data_please', function(e,mult) {
bar = {};
bar.v1 = base.private_obj.value1 *mult;
bar.v2 = base.private_obj.value2 *mult;
bar.v3 = base.private_obj.value3 *mult;
base.$el.trigger("here_is_the_multiplied_data", bar);
});
};
base.init();
}
$.foo.defaultOptions = {
opt1: 150,
opt2: 30,
opt3: 100
};
$.fn.foo = function(options){
return this.each(function(){
(new $.foo(this, options));
});
};
})(jQuery);
So, you can attach the object to an element as usual when the document is ready. And at the same time set up a handler for the result event.
$(document).ready(function(){
$('body').foo();
$('body').live('here_is_the_multiplied_data', function(e, data){
console.log("val1:" +data.v1);
console.log("val2:" +data.v2);
console.log("val3:" +data.v3);
$("#my_paragraph").html(data.v1 +" "+ data.v2+" " +data.v3);
});
})
All that's left is to trigger the event and pass it a multiplier value
You could type this into the console - or trigger it from a button that picks out the multiplier from another UI element
$('body').trigger('get_multiplied_data_please', 7);
Disclaimer ;) - I'm quite new to jQuery - sorry if this is using a hammer to crack a nut.
resizeBind function is defined as private so you cannot access it from outside of it's scope. If you want to use it in other scopes you need to define it like that
$.fn.resizeBind = function() { ... }
Then you would call it like that $(selector').resizeBind()
You have defined the resizeBind function in a scope that is different from the global scope. If you dont'use another javascript framework or anything else that uses the $ function (to prevent conflict) you can delete the
(function($){
...
})(jQuery);
statement and in this way the function will be callable everywhere without errors
I didn't test it:
this.resizeBind = function() { .... }

How do I create methods for an HTML element?

I'm trying to create a simple, small and basic javascript framework just for learning purposes.
But the thing is that i'm allready stuck at the very basics.
I'm trying to do something like this:
$('testdiv').testFunction();
And the code i've written for that:
var elementID;
var smallFramework = {
$:function(id) {
this.elementID = id;
},
testFunction:function() {
alert(this.elementID);
}
};
window.$ = smallFramework.$;
But in return I get:
$('testdiv) is undefined
Can anyone help me with this small and hopefully easy question?
To get the behavior you're expecting, you need the $ function to return an object with a method named testFunction.
Try:
var smallFramework = // an object for namespacing
{
$:function(id) // the core function - returns an object wrapping the id
{
return { // return an object literal
elementID: id, // holding the id passed in
testFunction: function() // and a simple method
{
alert(this.elementID);
}
};
}
};
Of course, there are many other ways to achieve the behavior you desire.
If you're trying to add methods to an HTML element you could do something along these lines.
$ = function( elementId ) {
var element = document.getElementById( elementId );
element.testFunction = function(){
alert( this.id );
return this; // for chaining
}
return element;
}
$('test').testFunction();
Try
smallFramework.$('testdiv');
instead. According to the code you posted, that's where your $ function ended up.
Or alternatively, it looks like you're trying to replicate something like jQuery. You might want to try something like this.
var $ = smallFramework = (function () {
var f =
{
find:function(id) {
f.elementID = id;
return f; //every function should return f, for chaining to work
},
testFunction:function() {
alert(f.elementID);
return f;
}
}
return f.find //the find function will be assigned to $.
//and also assigned to smallFramework.
//the find function returns f, so you get access to testFunction via chaining
// like $("blah").testFunction()
})() //note this function gets called immediately.
this code may look confusing to someone new to JavaScript because it depends heavily on the concept of closures. I suggest that if this doesn't make sense, spend some time at Douglas Crockford's JavaScript website. This is important because the code above will bite if you happen to use this in the find function because this won't be bound to f, as you may expect it to be when you use it from $ or smallFramework.

Categories

Resources