I'm writing a simple jQuery plugin, but I'm having trouble being able to use multiple instances on a page.
For instance, here is a sample plugin to illustrate my point:
(function($) {
$.fn.samplePlugin = function(options) {
if (typeof foo != 'undefined')
{
alert('Already defined!');
} else {
var foo = 'bar';
}
};
})(jQuery);
And then if I do this:
$(document).ready(function(){
$('#myDiv').samplePlugin({}); // does nothing
$('#myDiv2').samplePlugion({}); // alerts "Already defined!"
});
This is obviously an over-simplified example to get across the point. So my question is, how do I have two separate instances of the plugin? I'd like to be able to use it across multiple instances on the same page.
I'm guessing that part of the problem might be with defining the variables in a global scope. How can I define them unique to that instance of the plugin then?
Thank you for your guidance!
I have the very same problem but i find a very handy solution i´ll post it for someone who may have this problem
when you define your variables insinde the plugin you could use the .data() to store all the variables you define
like this
(function($) {
$.fn.samplePlugin = function(options) {
var base = this;
this.foo // define foo
// do stuff with foo and other variables
// Add a reverse reference to the DOM object
this.data("pluginname", base);
};})(jQuery);
And when you want to use the same foo variable you should retrive the reference with this:
base = this.data("pluginname");
base.foo
Hope it helps
Logan
html:
<code class="resize1">resize1</code>
<code class="resize2">resize2</code>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('.resize1').ratiofix({message:'resize1'});
$('.resize2').ratiofix({message:'resize2'});
});
</script>
I have found 2 solutions - the first one is jquery widget factory
http://jqueryui.com/widget/
js code:
$.widget("custom.ratiofix",{
options:{
message:"nothing"
},
_create:function (){
var self=this;
this.setListeners();
},
setListeners:function (){
var self=this;
$(window).on('resize',$.proxy(self.printMsg,self));
},
printMsg:function (){
console.log(this.options.message);
}
});
And the second (without widget factory):
(function ($){
var Ratiofix = {
init: function(options, elem) {
this.options = $.extend({},this.options,options);
this.elem = elem;
this.$elem = $(elem);
this.setListeners();
return this;
},
options: {
message: "No message"
},
printMsg: function(){
console.log(this.options.message);
},
setListeners:function (){
var self=this;
this.$elem.on('click',function (){
console.log(self.options.message);
});
$(window).on('resize',$.proxy(self.printMsg, self));
}
};
$.fn.ratiofix=function (options){
this.init= function(options, elem) {
this.options = $.extend({},this.options,options);
this.elem = elem;
this.$elem = $(elem);
return this;
};
if ( this.length ) {
return this.each(function(){
var ratiofix = Object.create(Ratiofix);
ratiofix.init(options, this);
$.data(this, 'ratiofix', ratiofix);
});
}
};
})(jQuery);
In both cases plugins work separately and have own settings. In my case - 2 widgets listen to window resize and print to console own options.message
I'm not sure what you mean by having more than one instance of a plugin. A plugin would be available to use on any element.
This comment doesn't clarify much for me:
So say that it was a plugin that took
a "color" parameter and turned the
object into that color. Well, in that
case you'd need multiple instances, as
you're dealing with more than one page
element turning more than one color.
In this case, you would pass in different colors are arguments as needed:
$('div#foo').makeColor('red');
$('div#bar').makeColor('blue');
Each time you call the plugin, it will use whatever arguments you give it. The plugin isn't a class that needs instances.
Just throwing my solution in here:
(function ($){
$.fn.plugin = function (options){
var settings = $.extend({}, $.fn.plugin.defaults, options);
settings.that = $(this);
$.fn.plugin.init (settings);
};
$.fn.plugin.defaults = { objval: 'default' };
$.fn.plugin.init = function (settings){
settings.that.val (settings.objval);
};
}( jQuery ));
$('#target1').plugin ({objval: 'not default'});
$('#target2').plugin ();
DEMO
The settings variable is isolated every time you initialize the object.
To answer your question directly, you can use jQuery.noconflict() to avoid namespace collisions and thus potentially have multiple instantiations on a page..
var $j = jQuery.noConflict();
// Use jQuery via $j(...)
$j(document).ready(function() {
// etc
check here
But I question your design. Why are you writing a plugin that appears to not operate on a jQuery wrapped set ? .. Plugins should be written to assume they are operating on a jQuery array held in 'this'. In which case any state can be stored in each of the items being acted upon... But maybe you are building something different?
Please review this page
instead of writing this
$("#divid1").samplePlugin();
$("#divid2").samplePlugin();
you can do this way
$.plugin('samplePlugin1', samplePlugin);
$("#divid1").samplePlugin1();
$.plugin('samplePlugin2', samplePlugin);
$("#divid2").samplePlugin2();
You can have much details from here
http://alexsexton.com/?p=51
You need to use this.foo instead of var foo, so that the variable is only related to the current object.
This worked a treat for me! I had specific parameters for which pages/places I wanted to run a plugin and was able to achieve success by using a simple if statement. Hope this helps someone!
<!-- Begin JQuery Plugin Foo -->
<script src="js/foo_fun.js"></script>
<?php
if(substr_count(strtolower($currentUrl),"member")>0)
{
?>
<script>
$(document).ready(function(){
$('#vscroller').vscroller({newsfeed:'news_employee.xml', speed:1000,stay:2000,cache:false});
});
</script>
<?php
}
else
{
?>
<script>
$(document).ready(function(){
$('#vscroller').vscroller({newsfeed:'news_company.xml', speed:1000,stay:2000,cache:false});
});
</script>
<?php
}
?>
<!-- End JQuery Foo-->
I had the same problem : how to use many instances of a plugin on only one form ?
The usual way fails because in fact, the instance is not an instance of the plugin : it is an instance of jQuery.
So, if more than one element is defined to be managed by a plugin, each definition overrides the previous parameters.
It was necessary to have a look on the problem from another side.
A plugin is usually made to react on a specific event for a specific element. e.g.. onclick on a button, or when the mouse is over the element.
In my case, I had to use an autocomplete plugin for a city field, but my form has 5 tabs and in total 4 fields for the cities for 4 different parts of the information to be collected.
For each fields, parameters are specifics.
By the way, I've realised iI don't need to have the plugin active everytime : just on the appropriate event on the field is enough.
So I had an idea : an event manager for each element. When the event appends, so I define the plugin action.
Some code will be more efficient to explain : imagine you have 3 div blocks and your plugin must change the colours, but with specifics colours depending on which div is affected.
$(document).ready(function(){
// Wich elements are affected by the plugin
var ids = ['myDiv1','myDiv2','myDiv3'];
// foe each one :
for (v in ids)
{
//define from an event :
$('#'+ ids[v]).focus(function()
{
// depending which id is active :
var aParams, idDiv = $(this).attr('id');
// Choosing the right params
switch(idDiv)
{
case 'myDiv1':
aParams = {'color': '#660000', 'background-color': '#0000ff'};
break;
case 'myDiv2':
aParams = {'color': '#006600', 'background-color': '#ff00ff'};
break;
case 'myDiv3':
aParams = {'color': '#000066', 'background-color': '#ff0000'};
break;
default:
aParams = {'color': '#000000', 'background-color': '#ffffff'};
};
// Defining the plugin on the right element with the right params
$(this).myPlugin(
{
colors: aParams
});
});
}
});
And this works fine.
Sorry if my English is not perfect - I hope you understand well.
Related
I'm trying to create a simple click catcher where if you click .image-class the javascript will take the href from another element with a class name of .btn and send you to it's destination. Though I keep getting errors on lines 7 & 10 saying that undefined is not a function. How do I make this work?
<script>
var ClickCatcher=
{
init:function(){
var link = jQuery('.btn')[1].href;
var imgCatch = jQuery('.image-class');
imgCatch.addEventListener("click", ClickCatcher.clickListener, false);
},
clickListener:function(){
window.location = link;
}
};
ClickCatcher.init();
</script>
You can do this with jquery with a simple click event
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
But if you still want to write in the way you have you can do:
var ClickCatcher = {
init: function () {
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
}
};
ClickCatcher.init();
Just make sure to fire the init method after dom load.
update: One issue with it is that you have coded your target etc in the code rather then pass it, so its going to be hard to reuse, you'd be better off doing:
var ClickCatcher = {
init: function ($button, loc) {
$button.on('click', function (){
window.location = loc;
});
}
};
ClickCatcher.init(jQuery('.image-class'), jQuery('.btn').eq(1).attr('href'));
That way the internal working is seperate from the dom (as you are passing the dom dependencies to the function.
#atmd showed a very good way of doing this. If you just want to know what your mistake was though. It is wa an error in your jQuery stament to get the btn href
jQuery('.btn')[1].href
you need to call the attr function and then get the href attr. and use .eq(1) to reduce the set to the first btn
jQuery('.btn').eq(1).attr('href);
Here is my plugin code below.
$.fn.myplugin = function(options) {
var defaults = {
my_data_title11:"",
my_div:".getslow",
}
var settings = $.extend({}, defaults, options);
return this.each(function(){
console.log(options.my_data_title);
});
}
I am calling this plugin from an external page like this...
$('#o_vari,#0_var2').myplugin({
my_data_title11:$(this).attr("id"),my_div:'.getfast'
});
but it is displaying undefined. I am aware my_data_title11:$(this).attr("id") is not recognizing $(this) the calling statement, I even tried putting this exteral variable , but still the same problem.
From the way you have written the code, I'm assuming you wants to print o_vari and 0_var2... but that is not the case because in the context in which you have used $(this).att('id'), this does not refer to the o_varx element.
So if you want the desired output try
$('#o_vari,#0_var2').each(function () {
$(this).myplugin({
my_data_title: this.id,
my_div: '.getfast'
});
})
Demo: Fiddle
I'm trying to get the jquery loadmask addon to work that will mask elements (for loading content). I'm using knockout.js, and when if I mask an element outside of my viewmodel it works, but I want to mask it upon submitting a POST request, and then unmask when I receive it. I'm getting an "object has no method mask" error from this. I'm not quite sure how to go about setting up an object to access it.
This works, but it's not what I want. I noted in the code where I would like to call mask from
<div id = "register_container">
<div data-bind="visible: register()">
<div id = "register_form"> <!--this is the div I want to mask -->>
<button data-bind="click: submitRegistration">Submit</button>
</div>
</div>
</div>
function MyViewModel(){
self.submitRegistration = function(){
//I want to mask here. When I try it says Object[object object] has no method mask
$.post....{
if(data.result == success){
// andunmask here
}
}
}
}
$("#register_form").mask("Waiting..."); //the masking works when I place it here, but it's always enabled and I want it inside the viewmodel where I noted so it only works when the POST request is in process
That's great and all, but I want to mask something from inside the viewmodel where I noted. How can I accomplish this?
I see several things that could be the problem.
Frist, you're doing assignment as opposed to comparison in the if statement. Use this instead:
if(data.result == success){
or even
if(data.result === success){
Second is the fact that I don't quite understand your code self.submitRegistration(){, which typically looks more like this:
var MyViewModel = function () {
var self = this;
self.submitRegistration = function() {
};
};
Then, if I mock the $.post call, it would work like this:
var MyViewModel = function () {
var self = this;
self.register = ko.observable(true);
self.submitRegistration = function() {
$("#register_form").mask("Waiting...");
// Mock $.post
window.setTimeout(function () {
if (1 == 1) {
// andunmask here
$("#register_form").unmask();
}
}, 3000);
}
};
ko.applyBindings(new MyViewModel());
See this fiddle for a demo.
You could even have Knockout help you find the element to look for:
See this updated fiddle for a demo of that.
// Use the "event" parameter to find the element...
self.submitRegistration = function(data, event) {
$(event.target).closest('#register_form').mask("Waiting...");
Hope it helps.
I am trying to work out how to call functions within my jQuery plugin from outside the plugin. The code I have tried is not working. I'm sure I will have to restructure my plugin to allow this, but I'm not sure how to. In this example, I'm trying to access the underline() function.
jsFiddle
jQuery plugin
(function($) {
"use strict";
$.fn.testPlugin = function(options) {
// Settings
var settings = $.extend({
newText : "Yabadabado"
}, options);
return this.each(function(i, el) {
var init = function(callback) {
if( $(el).attr("class") === "red" ) {
$(el).css("color","red");
}
$(el).text(settings.newText);
if( callback && typeof(callback) === "function" ) {
callback();
}
};
var underline = function() {
$(el).addClass("underline");
};
init();
});
};
}(jQuery));
Assign the plugin to selectors
var doTest = $("#testItem").testPlugin({
newText: "Scoobydoo"
});
var doNewTest = $("#newTestItem").testPlugin({
newText: "kapow!"
});
Call a function that is located within the plugin
$("#underline").click(function(e) {
e.preventDefault();
doTest.underline();
});
Take a look at closures.
Here is a basic example of what a closure looks like in a jQuery plugin.
$.fn.plugin = function() {
return {
helloWorld: function() {
console.log('Hello World!');
}
}
};
// init plugin.
var test = $('node').plugin();
// call a method from within the plugin outside of the plugin.
test.helloWorld();
You can see another example at the following jsfiddle.
http://jsfiddle.net/denniswaltermartinez/DwEFz/
First thing first we need to understand each step in building a jQuery plugin, its like build a javascript plugin (class) but we have in addition to it a jQuery class.
//We start with a function and pass a jQuery class to it as a
//parameter $ to avoid the conflict with other javascript
//plugins that uses '$ as a name
(function($){
//We now append our function to the jQuery namespace,
//with an option parameter
$.fn.myplugin = function(options) {
//the settings parameter will be our private parameter to our function
//'myplugin', using jQuery.extend append 'options' to our settings
var settings = jQuery.extend({
param:'value',
}, options);
//Define a reference to our function myplugin which it's
//part of jQuery namespace functions, so we can use later
//within inside functions
var $jquery=this;
//Define an output object that will work as a reference
//for our function
var output={
//Setup our plugin functions as an object elements
'function1':function(param){
//Call jQuery reference that goes through jQuery selector
$jquery.each(function(){
//Define a reference of each element of jQuery
//selector elements
var _this=this;
});
//This steps is required if you want to call nested
//functions like jQuery.
return output;
},
//If we want to make our plugin to do a specific operations
//when called, we define a function for that
'init':function(){
$jquery.each(function(){
var _this=this;
//Note that _this param linked to each jQuery
//functions not element, thus wont behave like
//jQuery function.
//And for that we set a parameter to reference the
//jQuery element
_this.$this=$(this);
//We can define a private function for 'init'
//function
var privatefun=function(){}
privatefun();
//We can now do jQuery stuffs on each element
_this.$this.on('click',function(){
//jQuery related stuffs
});
});
//We can call whatever function we want or parameter
//that belongs to our plugin
output.function1("value");
}
};
//Our output is ready, if we want our plugin to execute a
//function whenever it called we do it now
output.init();
//And the final critical step, return our object output to
//the plugin
return output;
};
//Pass the jQuery class so we can use it inside our plugin 'class'
})(jQuery);
Using our function now is very easy
<div class="plugintest">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
</div>
<script>
$(function(){
var myplugin=$(".plugintest > span").myplugin({
param:'somevalue'
});
myplugin.function1(1).function1(2).function1(3);
});
</script>
In short, jQuery plugins and any Javascript plugins are simply about parameters scope.
Fiddle version
https://jsfiddle.net/eiadsamman/a59uwmga/
I can't create a new element in the page. I check the page and domain when the page is onload, that's work, but I don't know how can I create a new element in the correct window page.
window.addEventListener("load", function() { myExtension.init(); }, false);
var myExtension = {
init: function() {
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", myExtension.onPageLoad, true);
},
onPageLoad: function(aEvent) {
var unsafeWin = aEvent.target.defaultView;
if (unsafeWin.wrappedJSObject) unsafeWin = unsafeWin.wrappedJSObject;
var locationis = new XPCNativeWrapper(unsafeWin, "location").location;
var hostis = locationis.host;
//alert(hostis);
if(hostis=='domain.com')
{
var pathnameis=locationis.pathname;
if(pathnameis=='/index.php')
{
$("#left .box:eq(0)").after('<div id="organic-tabs" class="box"></div>'); // this code somewhy doesn't working, but if I copy to FireBug it't work.
}
}
}
}
My question is: How can I use Javascript and jQuery from firefox addon when I want to manipulate html in the correct window content? What is need from here
$("#left .box:eq(0)").after('<div id="organic-tabs" class="box"></div>');
for working.
This code has a bunch of issues. For one, appcontent is not the browser, gBrowser is. So it should be:
init: function() {
gBrowser.addEventListener("DOMContentLoaded", myExtension.onPageLoad, true);
},
Then, using wrappedJSObject is absolutely unnecessary (and also not safe the way you do it).
var wnd = aEvent.target.defaultView;
var locationis = wnd.location;
Finally, you are trying to select an element in the browser document (the document that your script is running in), not in the document loaded into the tab. You need to give jQuery an explicit context to work on:
$("#left .box:eq(0)", wnd.document)
But you shouldn't use jQuery like that, it defines a number of global variables that might conflict with other extensions. Instead you should call jQuery.noConflict() and create an alias for jQuery within myExtension:
var myExtension = {
$: jQuery.noConflict(true),
....
myExtension.$("#left .box:eq(0)", wnd.document)
Here is a template you can use that incorporates your sample code. I also added an additional statement so you could see another use of jQuery. Important points:
You must load jQuery before you can use it. You should myplace the jQuery library file you want to use in Chrome, for example, in the chrome/content directory.
Use window.content.document as the context for every jQuery
operation on the contents of the Web page
Use this as the context of a successful search result to help you
insert code in the correct spot.
window.addEventListener('load', myExtension.init, false);
var myExtension = {
jq : null,
init : function() {
var app;
// Load jQuery
var loader = Components.classes["#mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://myExtension/content/jquery-1.5.2.min.js");
myExtension.jq = jQuery.noConflict();
// Launch extension
if ((app = document.getElementById("appcontent"))) {
app.addEventListener("DOMContentLoaded", myExtension.run, true);
}
},
run : function() {
// make sure this is the correct Web page to change
var href = event.originalTarget.location.href;
if (href && href.match(/http:\/\/(www\.)?domain\.com\/(index\.php)/i)) {
changeScreen();
}
},
changeScreen : function() {
// make changes to the screen
// note the "window.content.document) in the first jQuery selection
myExtension.jq("#left .box:eq(0)", window.content.document).after('');
// note the use of "this" to use the search results as the context
myExtension.jq("#right", window.content.document).each(function() {
myExtension.jq("tr td", this).append('MATCH!');
});
}
}