Calling member function inside of Jquery Plugin - javascript

I have created a basic plugin template based on the code generated here:
http://starter.pixelgraphics.us/
Here is a link to the very basic skeleton:
https://jsbin.com/yatofefade/edit?html,js,console,output
$.curationPanel = function( el, options ) {
var base = this;
base.$el = $(el);
base.el = el;
base.$el.data( "curationPanel", base );
base.init = function( ) {
base.options =
$.extend( {}, $.curationPanel.defaultOptions, options );
};
base.runMe = function( ) {
alert( "I've Been Run" );
};
base.init( );
};
$.curationPanel.defaultOptions = { };
$.fn.curationPanel = function( options ) {
return this.each( function( ) {
(new $.curationPanel( this, options ));
});
};
$(".curationPanel").each( function( i, val ) {
var cp = $(this).curationPanel({});
cp.runMe( );
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="curationPanel">INSIDE THE PANEL<div class="curationErrors"></div></div>
</body>
</html>
My question is, why do I get an error when trying to call runMe( ) on a created instance of curationPanel? What is the correct way to create callable public functions within the plugin?

In your case cp is a jQuery object, not an instance of the curationPanel since you are returning this from the plugin method, that is why the error.
There are multiple ways to do this.
One way is to break the chaining nature of the jQuery and return an instance of the plugin object as shown below. Other than breaking the chaining nature of jQuery method, another drawback of this design is, at any call you can use this to initialize the plugin for only one element, ie if you have a selector which selects more than 1 element and then call the plugin, the plugin will be initialized for only the first element.
$.curationPanel = function(el, options) {
var base = this;
base.$el = $(el);
base.el = el;
base.$el.data("curationPanel", base);
base.init = function() {
base.options = $.extend({}, $.curationPanel.defaultOptions, options);
};
base.runMe = function() {
snippet.log("I've Been Run");
};
base.init();
};
$.curationPanel.defaultOptions = {};
$.fn.curationPanel = function(options) {
return new $.curationPanel(this[0], options);
};
$(".curationPanel").each(function(i, val) {
var cp = $(this).curationPanel({});
cp.runMe();
});
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="curationPanel">INSIDE THE PANEL
<div class="curationErrors"></div>
</div>
Another way is to obtain the plugin instance using the data api like
$.curationPanel = function(el, options) {
var base = this;
base.$el = $(el);
base.el = el;
base.$el.data("curationPanel", base);
base.init = function() {
base.options = $.extend({}, $.curationPanel.defaultOptions, options);
};
base.runMe = function() {
snippet.log("I've Been Run");
};
base.init();
};
$.curationPanel.defaultOptions = {};
$.fn.curationPanel = function(options) {
return this.each(function() {
(new $.curationPanel(this, options));
});
};
$(".curationPanel").each(function(i, val) {
var cp = $(this).curationPanel({});
$(this).data('curationPanel').runMe();
});
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="curationPanel">INSIDE THE PANEL
<div class="curationErrors"></div>
</div>

Related

AddEventListener with in a object

I could need your help, at the moment I am trying to call a method within a object through a addEventListener activation. The problem is that the this pointer is changing as soon as the method get called. I tried to use the .call to give the method the right context back and it worked as expected but is this the best way ?
<!doctype html>
<html>
<head>
<title>Java Script Test</title>
<meta charset="UTF-8" />
<!-- <script src="//code.jquery.com/jquery-1.11.2.min.js"></script> -->
<script src="../resurces/js/jquery-1.11.2.min.js" ></script>
<script>
var test_class = function(){
this.variable = "more text";
this.click = function(){
$("#return").text("Content: "+this.variable);
}
this.aktivate_listener = function(){
var that = this;
document.getElementById("clicker").addEventListener("click", function(){that.click.call(that)});
}
}
$("document").ready(function(){
console.log("#ready");
var class1 = new test_class();
class1.aktivate_listener();
});
</script>
</head>
<body>
<p id="clicker">This is some Text to click on.</p>
<p id="return"></p>
</body>
</html>
Do some of you know a better way ?
Thanks for your effort ,
Flo
That's mostly fine, but a couple of points:
You don't need to do that.click.call(that) — simply that.click() will work.
You're not passing on the event object that your anonymous function receives. If you don't need it, that's fine, but I thought I'd mention it.
Another alternative is to use Function#bind:
this.aktivate_listener = function(){
document.getElementById("clicker").addEventListener("click", this.click.bind(this));
}
Function#bind returns a function that, when called, turns around and calls the original function with this set to a specific value. So this.click.bind(this) creates a function that, when called, will call click on the object with this referring to the object. Any arguments to the bound function are passed on when it calls the underlying function.
Re your comment:
But I read that you can't remove an EventListener that is created with .bind A problem in the example above is that you cannot remove the listener with bind. That is taken from the Mozilla Developer Network.
If so, MDN is wrong. It's community-edited, that happens. :-)
In terms of being able to remove the handler with removeEventListener, there's no difference at all between your code and using Function#bind as above: You can't remove the listener in either case without changing the code to remember what we passed to addEventListener.
There's nothing special about event handlers that use bind. Just like any other event handler, if you want to remove it with removeEventListener, you have to have a reference to the same function you added when removing it. In your code, that would be the anonymous function that you have wrapped around that.click.call(that);, and since you didn't keep a reference to it, you can't remove it. Similarly, in my code above, you can't remove the listener because I didn't keep a reference to the bound function.
If that's something you need to do, just remember the function you want to remove — that's a reference to your anonymous function, or a reference to the function returned by Function#bind. You might store it on your object, for instance.
this.aktivate_listener = function(){
if (this.boundClick) {
this.deaktivate_listener();
}
this.boundClick = this.click.bind(this);
document.getElementById("clicker").addEventListener("click", this.boundClick);
};
this.deacktivate_listener = function(){
document.getElementById("clicker").removeEventListener("click", this.boundClick);
this.boundClick = null;
};
Looking again at your code, you have a third option: Your click function is a closure over the call to test_class that created the instance, so you don't need to create another closure, just use the one you already have:
var test_class = function(){
// Remember `this` in a variable
var self = this;
this.variable = "more text";
this.click = function(){
// Use it here
$("#return").text("Content: "+self.variable);
};
this.aktivate_listener = function(){
// Just use `this.click` here
document.getElementById("clicker").addEventListener("click", this.click);
};
this.deaktivate_listener = function(){
// Just use `this.click` here
document.getElementById("clicker").removeEventListener("click", this.click);
};
};
Side note: You need a ; at the end of statements like var f = function() { };, because those are statements, not declarations. I've added them above. If you don't provide it, Automatic Semicolon Insertion will add it for you most of the time, but you can trip up if you're not careful.
Examples of all of the above:
Your way without .call (without deaktivate):
var Test = function(id, name) {
this.id = id;
this.name = name;
this.click = function() {
snippet.log("My name is " + this.name);
};
this.aktivate_listener = function() {
var that = this;
document.getElementById(this.id).addEventListener(
"click",
function() { that.click(); },
false
);
};
};
var t1 = new Test("one", "test one");
t1.aktivate_listener();
var t2 = new Test("two", "test two");
t2.aktivate_listener();
<div id="one">Click me (one)</div>
<div id="two">Click me (two)</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
With Function#bind (without deaktivate):
var Test = function(id, name) {
this.id = id;
this.name = name;
this.click = function() {
snippet.log("My name is " + this.name);
};
this.aktivate_listener = function() {
document.getElementById(this.id).addEventListener(
"click",
this.click.bind(this),
false
);
};
};
var t1 = new Test("one", "test one");
t1.aktivate_listener();
var t2 = new Test("two", "test two");
t2.aktivate_listener();
<div id="one">Click me (one)</div>
<div id="two">Click me (two)</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Using the existing closure (without deaktivate):
var Test = function(id, name) {
var self = this;
this.id = id;
this.name = name;
this.click = function() {
snippet.log("My name is " + self.name);
};
this.aktivate_listener = function() {
document.getElementById(this.id).addEventListener(
"click",
this.click,
false
);
};
};
var t1 = new Test("one", "test one");
t1.aktivate_listener();
var t2 = new Test("two", "test two");
t2.aktivate_listener();
<div id="one">Click me (one)</div>
<div id="two">Click me (two)</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Your way without .call (with deaktivate):
var Test = function(id, name) {
this.id = id;
this.name = name;
this.counter = 0;
this.click = function() {
snippet.log("My name is " + this.name);
if (++this.counter == 2) {
this.deaktivate_listener();
};
};
this.aktivate_listener = function() {
var that = this;
if (this.boundClick) {
this.deaktivate_listener();
}
this.boundClick = function() { that.click(); };
document.getElementById(this.id).addEventListener(
"click",
this.boundClick,
false
);
};
this.deaktivate_listener = function() {
if (this.boundClick) {
document.getElementById(this.id).removeEventListener(
"click",
this.boundClick,
false
);
this.boundClick = null;
}
};
};
var t1 = new Test("one", "test one");
t1.aktivate_listener();
var t2 = new Test("two", "test two");
t2.aktivate_listener();
<div id="one">Click me (one) (second click deactivates)</div>
<div id="two">Click me (two) (second click deactivates)</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
With Function#bind (with deaktivate):
var Test = function(id, name) {
this.id = id;
this.name = name;
this.counter = 0;
this.click = function() {
snippet.log("My name is " + this.name);
if (++this.counter == 2) {
this.deaktivate_listener();
};
};
this.aktivate_listener = function() {
if (this.boundClick) {
this.deaktivate_listener();
}
this.boundClick = this.click.bind(this);
document.getElementById(this.id).addEventListener(
"click",
this.boundClick,
false
);
};
this.deaktivate_listener = function() {
if (this.boundClick) {
document.getElementById(this.id).removeEventListener(
"click",
this.boundClick,
false
);
this.boundClick = null;
}
};
};
var t1 = new Test("one", "test one");
t1.aktivate_listener();
var t2 = new Test("two", "test two");
t2.aktivate_listener();
<div id="one">Click me (one) (second click deactivates)</div>
<div id="two">Click me (two) (second click deactivates)</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Using the existing closure (with deaktivate):
var Test = function(id, name) {
var self = this;
this.id = id;
this.name = name;
this.counter = 0;
this.click = function() {
snippet.log("My name is " + self.name);
if (++self.counter == 2) {
self.deaktivate_listener();
};
};
this.aktivate_listener = function() {
document.getElementById(this.id).addEventListener(
"click",
this.click,
false
);
};
this.deaktivate_listener = function() {
document.getElementById(this.id).removeEventListener(
"click",
this.click,
false
);
};
};
var t1 = new Test("one", "test one");
t1.aktivate_listener();
var t2 = new Test("two", "test two");
t2.aktivate_listener();
<div id="one">Click me (one) (second click deactivates)</div>
<div id="two">Click me (two) (second click deactivates)</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Here is the version I use at the moment, it is made of #T.J. Crowder answers.
function boxOperator(){
this.event = [];
this.addEvent = function(element, event, tocall_function, arg){
var call_function = tocall_function.bind(this, arg);
this.event[this.event.length] = {
"element" : element,
"event" : event,
"call_function" : call_function
};
document.getElementById(element).addEventListener(event,call_function);
};
this.removeEvent = function(element, event){
if(!element){return false;};
var remove_entry = function(index, array){return array.slice(0,index-1).concat(array.slice(index+1));}
for(var i = 0; i < this.event.length;i++){
if(this.event[i].element == element){
var entry = this.event[i];
if(entry.event == event){
document.getElementById(entry.element).removeEventListener(entry.event,entry.call_function);
this.event = remove_entry(i, this.event);
}
if(typeof event == "undefined"){
document.getElementById(entry.element).removeEventListener(entry.event,entry.call_function);
this.event = remove_entry(i, this.event);
}
}
}
};
}

Unable to target a selector using Namespace ,Modular, Private

i am trying to target a jquery selector by using namespaces in my script and also making function private but i think i am still missing something here, can anyone guide. It works if i try by adding a breakpoint on the last line and than use devtools to access MyUtility.Selectors.ColorCss.myBorder()
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Selectors</title>
</head>
<body>
<ul>
<li class="test">First</li>
<li>Second</li>
<li>Third</li>
</ul>
<!--<script>
$('document').ready(function(){
$('li.test').css('color','green')
})
</script>-->
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script>
var customModule = (function () {
var MyUtility = {
Selectors: {
ColorCss: function () {
var myBorder = function () {
$('li').css('color', 'red')
console.log('hello')
}
return{
myBorder: myBorder
}
}()
}
}
}())
</script>
</body>
</html>
As you said It works if i try by adding a breakpoint on the last line and than use devtools to access MyUtility.Selectors.ColorCss.myBorder()
This is your code:
var customModule = (function () {
var MyUtility = {
Selectors: {
ColorCss: function(){
var myBorder = function(){
$('li').css('color', 'red');
console.log('hello');
}
return{ myBorder: myBorder }
}()
} // Selectors
} // MyUtility
}())
Your code above can be written as:
function myBorderFunc() { $('li').css('color', 'red'); console.log('hello');}
var selectorObj = { ColorCss : function(){ return{ myBorder: myBorderFunc } }()};
var MyUtility = { Selectors: selectorObj};
var customModule = ( function(){ MyUtility }() );
This shows the problem
var customModule is a function expression that does not return anything and it is therefore undefined
since customModule is undefined you can not use customModule.MyUtility
as you said you can call MyUtility.Selectors.ColorCss.myBorder() since MyUtility is an object that has a property Selectors and so on
you can test it out with this example:
// undefined since nothing is returned
var bar = (function(){ {Foo: "i am foo"} }());
// returns foo and can be used bar.Foo ---> "i am foo"
var bar = (function(){ return {Foo: "i am foo"} }());
To 'fix your code' return MyUtility
var customModule = (function () {
var MyUtility = {
Selectors: {
ColorCss: function(){
var myBorder = function(){
$('li').css('color', 'red');
console.log('hello');
}
return{ myBorder: myBorder }
}()
} // Selectors
} // MyUtility
return MyUtility;
}())
This way you can access it like this customModule.Selectors.ColorCss.myBorder().
More info about Function expressions vs. Function declarations

How can each instance of a jQuery plugin have its own properties and methods?

I am learning how to create jQuery plugins and have built one using module pattern. It works if I apply it only once, however, if I apply multiple times, all of them get initialized using the last one's settings.
For instance, if I first do $('#div1').myPlugin();, and then later $('#div2').myPlugin({myProperty :'mydiv2Property'});, $('#div1') myProperty is changed from myDefaultProperty to mydiv2Property. The same thing happens when initializing with a different method.
I have a working (well, almost working!) example located at http://jsbin.com/eWePoro/1/, and my full script is listed below.
How do I change this script so each time the plugin is applied, it uses just its own properties and methods? Thank you
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Testing</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js" type="text/javascript"></script>
<style type="text/css">
</style>
<script type="text/javascript">
(function($){
var defaults = {
myProperty :'myDefaultProperty',
myMethod1 :function () {
console.log('myMethod1',this,this.myProperty);
},
myMethod2 :function () {
console.log('myMethod2',this,this.myProperty);
}
};
var methods = {
init : function (options) {
var settings = $.extend(defaults, options || {});
settings.myMethod1();
return this.each(function () {
$(this).click(function(e) {
settings.myMethod2();
});
});
},
destroy : function () {
//Anything else I should do here?
delete settings;
return this.each(function () {});
}
};
$.fn.myPlugin = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || ! method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.myPlugin');
}
};
}(jQuery)
);
$(function(){
$('#div1').myPlugin();
$('#div2').myPlugin({
myProperty :'mydiv2Property'
});
$('#div3').myPlugin({
myMethod1 :function () {console.log('myMethod1_new',this,this.myProperty);}
});
$('#div4').myPlugin({
myMethod2 :function () {console.log('myMethod2_new',this,this.myProperty);}
});
});
</script>
</head>
<body>
<div id='div1'>div1</div>
<div id='div2'>div2</div>
<div id='div3'>div3</div>
<div id='div4'>div4</div>
</body>
</html>
The problem is here:
var settings = $.extend(defaults, options || {});
You are actually modifying defaults here with new properties. So next time you run the same code, defaults will be mutated. You should probably do:
var settings = $.extend({}, defaults, options);
This will create a new settings object every time by cloning defaults before extending it.
DEMO: http://jsbin.com/eWePoro/2

Call the same instance of jQuery plugin

I have written a jQuery plugin below and would like to be able to call it again for the same instance on an element.
The plugin goes...
(function($) {
$.fn.myPlugin = function(options){
var settings = {
color: null
};
if (options) {
$.extend(settings, options);
}
return this.each(function(){
var self = this;
var pics = $('li', self);
function refresh() {
pics = $('li', self);
};
$('a', self).click(function(){
pics.filter(':last').remove();
alert(settings.color);
refresh();
return false;
});
});
}
})(jQuery);
In the page this is called...
$('#test').myPlugin({ color: 'blue' });
Now I want to call the same plugin for the same instance but pass the string refresh as the option whilst all the other variables are the same (so color would still be blue) e.g...
$('#test').myPlugin('refresh');
This would then execute the refresh() function.
How could I achieve that with the above?
Edit: To make it clearer I am thinking of how jQuery UI does their plugins. In the sortable plugin you can do $("#sortable").sortable(); and then $("#sortable").sortable('refresh'); on the same element. This is what I am trying to achieve.
You can store your instance with .data() and check for it when creating an instance.
Something like:
$.fn.doStuff = function () {
var ob = $(this);
var data = ob.data();
if (data.doStuff !== undefined) {
return data.doStuff;
}
doStuff;
});
(function($) {
$.fn.myPlugin = function(options){
var init = function($self, ops){
$self.find("a").click(function(){
pics.filter(':last').remove();
alert(settings.color);
refresh();
return false;
});
};
this.refresh = function(){
//your code here
};
return this.each(function(){
var self = this;
var pics = $('li', self);
var settings = {
color: null
};
var ops = $.extend(true, settings, options);
init($(this), ops);
});
}
})(jQuery);
try something like this. and you can call refresh() like $().myPlugin().refresh();

plugin methods - what's the magic here?

I struggled and finally got [this] to work. now, I wanted to break it up as shown below but it doesn't work... is there some voodoo here I don't understand?
<!DOCTYPE html>
<html>
<head>
<!-- jQuery -->
<script type="text/javascript" src="http://goo.gl/XQPhA"></script>
<script type="text/javascript">
(function($) {
$.test = function(options) {
options = $.extend({}, $.test.settings, options);
this.whiten = function() {
$(this).css('background-color', options.bg);
};
};
$.test.settings = { bg: 'white' };
$.fn.test = function(options) {
return this.each(function(index, el) {
$.test(options);
});
};
})(jQuery);
$(document).ready(function() {
$('ul').test().css('background-color', 'wheat');
$('#go').click(function() {
$('ul').whiten();
});
});
</script>
</head>
<body>
<button id="go">whiten</button>
<ul id="list1">
<li>Aloe</li>
<li>Bergamot</li>
<li>Calendula</li>
<li>Damiana</li>
<li>Elderflower</li>
<li>Feverfew</li>
</ul>
<ul id="list2">
<li>Ginger</li>
<li>Hops</li>
<li>Iris</li>
<li>Juniper</li>
<li>Kavakava</li>
<li>Lavender</li>
<li>Marjoram</li>
<li>Nutmeg</li>
<li>Oregano</li>
<li>Pennroyal</li>
</ul>
</body>
</html>
as compared with the previous code, inside of the each() loop I call now $.test(options) instead of $.fn.test(options) - so why does one work and not the other (actually, why/how does the first one work to begin with)?
I would restructure your plugin to follow the guidelines outlined in the plugin authoring guide, most notably storing the data for the settings for your widget with .data() and making method calls to your plugin with .test("method"):
(function($) {
/* Default plugin settings: */
var settings = {
bg: 'white'
};
/* Method definitions: */
var methods = {
init: function(options) {
options = $.extend({}, options, settings);
return this.each(function () {
$(this).data("test", options);
});
},
whiten: function() {
var options = this.data("test");
this.css('background-color', options.bg);
}
};
/* Plugin definition and method calling logic: */
$.fn.test = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist');
}
}
})(jQuery);
Usage: $("elem").test(), $("elem").test("whiten")
Here's a working example: http://jsfiddle.net/z4R3X/
An additional resource for plugin authoring guidance is the jQueryUI source code (take the autocomplete widget for example). These widgets are pretty good examples of how to create reusable, readable jQuery plugins.

Categories

Resources