Jquery plugin callback on element selector - javascript

I'm creating a plugin that works with scrolling to detect when an element is visible in the viewport. I'm trying to add a callback option that executes anything that's passed in it when an element is targeted. Below are the main parts of my plugin.
$.myPlugin = function(el, options) {
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Initialize
base.init = function() {
base.options = $.extend({}, $.myPlugin.defaultOptions, options);
};
// when element is visible in the viewport
if (base.inviewport()) {
// Callback
if (typeof base.options.callback === 'function') {
base.options.callback.call(this);
}
return;
}
// Run initializer
base.init();
$.fn.myPlugin = function(options) {
return this.each(function() {
(new $.myPlugin(this, options));
});
};
}
Now I'm trying to use the callback:
$(function() {
$('.some-class').myPlugin({
callback: function() {
console.log($(this));
}
});
});
When I scroll to the element selector I'm expecting the callback to return the element with the class name some-class but instead it returns window
[Window, jquery: "1.10.2", constructor: function, init: function, selector: "", toArray: function…]
Please inform what I'm doing wrong...

Related

Window load inside self invoking jQuery plugin function

I have the following jQuery plugin code to do something on window load or scroll:
Note: I simplified the code for the example.
;(function ($, window, document, undefined) {
'use strict';
$.scrollFn = function (el, options) {
var base = this;
// jQuery and DOM of element
base.$el = $(el);
base.el = el;
// Cached
base.$win = $(window);
base.$doc = $(document);
// Initialize
base.init = function () {
base.options = $.extend({}, $.scrollFn.defaultOptions, options);
};
// Scroll handler
base.scrollHandler = function () {
console.log('scrolled or loaded');
console.log(base.options.exampleOption);
};
// On scroll and load
base.$win.on('scroll load', base.scrollHandler);
};
$.scrollFn.defaultOptions = {
exampleOption: "Test"
};
$.fn.scrollFn = function (options) {
return this.each(function () {
(new $.scrollFn(this, options));
});
};
})(jQuery, window, document);
And in another JS I am initializing the scroll function:
$(document).ready(function() {
$('.example').scrollFn({
exampleOption: "Hello world"
});
});
The issue: It is picking up window scroll but not load. When the window loads I do not see the console return scrolled or loaded. It shows only when I scroll.
How can I make load work as well?
I tried (inside scrollFn):
$(window).on('scroll load', base.scrollHandler);
base.$doc.on('scroll load', base.scrollHandler);
$(document).ready(function() {
base.$win.on('scroll load', base.scrollHandler);
});
They do not work.
I had to simply add base.scrollHandler in the init function. I will except any other answer providing more info or a better solution if possible.

jQuery plugin instances variable with event handlers

I am writing my first jQuery plugin which is a tree browser. It shall first show the top level elements and on click go deeper and show (depending on level) the children in a different way.
I got this up and running already. But now I want to implement a "back" functionality and for this I need to store an array of clicked elements for each instance of the tree browser (if multiple are on the page).
I know that I can put instance private variables with "this." in the plugin.
But if I assign an event handler of the onClick on a topic, how do I get this instance private variable? $(this) is referencing the clicked element at this moment.
Could please anyone give me an advise or a link to a tutorial how to get this done?
I only found tutorial for instance specific variables without event handlers involved.
Any help is appreciated.
Thanks in advance.
UPDATE: I cleaned out the huge code generation and kept the logical structure. This is my code:
(function ($) {
$.fn.myTreeBrowser = function (options) {
clickedElements = [];
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
titleAttribute: "Title",
idAttribute: "Id",
parentIdAttribute: "ParentId",
levelAttribute: "Level",
treeData: {}
};
var opts = $.extend({}, $.fn.myTreeBrowser.defaults, options);
function getTreeData(id) {
if (opts.data) {
$.ajax(opts.data, { async: false, data: { Id: id } }).success(function (resultdata) {
opts.treeData = resultdata;
});
}
}
function onClick() {
var id = $(this).attr('data-id');
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, id);
}
function handleOnClick(parentContainer, id) {
if (opts.onTopicClicked) {
opts.onTopicClicked(id);
}
clickedElements.push(id);
if (id) {
var clickedElement = $.grep(opts.treeData, function (n, i) { return n[opts.idAttribute] === id })[0];
switch (clickedElement[opts.levelAttribute]) {
case 1:
renderLevel2(parentContainer, clickedElement);
break;
case 3:
renderLevel3(parentContainer, clickedElement);
break;
default:
debug('invalid level element clicked');
}
} else {
renderTopLevel(parentContainer);
}
}
function getParentContainer(elem) {
return $(elem).parents('div.myBrowserContainer').parents()[0];
}
function onBackButtonClick() {
clickedElements.pop(); // remove actual element to get the one before
var lastClickedId = clickedElements.pop();
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, lastClickedId);
}
function renderLevel2(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderLevel3(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderTopLevel(parentContainer) {
parentContainer.html('');
var browsercontainer = $('<div>').addClass('fct-page-pa fct-bs-container-fluid pexPAs myBrowserContainer').appendTo(parentContainer);
// rendering the top level display
}
getTreeData();
//top level rendering! Lower levels are rendered in event handlers.
$(this).each(function () {
renderTopLevel($(this));
});
return this;
};
// Private function for debugging.
function debug(debugText) {
if (window.console && window.console.log) {
window.console.log(debugText);
}
};
}(jQuery));
Just use one more class variable and pass this to it. Usually I call it self. So var self = this; in constructor of your plugin Class and you are good to go.
Object oriented way:
function YourPlugin(){
var self = this;
}
YourPlugin.prototype = {
constructor: YourPlugin,
clickHandler: function(){
// here the self works
}
}
Check this Fiddle
Or simple way of passing data to eventHandler:
$( "#foo" ).bind( "click", {
self: this
}, function( event ) {
alert( event.data.self);
});
You could use the jQuery proxy function:
$(yourElement).bind("click", $.proxy(this.yourFunction, this));
You can then use this in yourFunction as the this in your plugin.

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();

Click Function on jQuery plugin only allows for single click

I've created this simple plugin to add multiple animations on click but the problem is that once the object is clicked it can not repeat the animation by clicking again, i can't figure out why the added class is not removing itself after the click function is complete to allow it to be clicked again and repeat.. any suggestions?
(function($) {
$.fn.vivify = function(options) {
var defaults = {
animation: 'bounce',
};
var options = $.extend(defaults, options);
return this.each(function() {
var o = options;
var obj = $(this);
var animation = o.animation;
obj.bind("click", function() {
obj.addClass(o.animation);
obj.addClass('vivify');
}, function() {
obj.removeClass(o.animation);
});
})
}
})(jQuery);​
Here's a working example (I guess, because I don't know exactly what's the intended effect of your plugin):
http://jsfiddle.net/gabrieleromanato/Fmr9g/
obj.bind("click", function() {
obj.addClass(o.animation);
obj.addClass('vivify');
},
// this callback is not valid
function() {
obj.removeClass(o.animation);
});
because .bind() accept parameters like following:
.bind(eventName, [eventData], [callback])
Read about .bind()
To remove class you can do:
obj.bind("click", function() {
obj.addClass(o.animation);
obj.addClass('vivify');
// you can removeClass here
obj.removeClass(o.animation);
// but you need some delay
setTimeou(function() {
obj.removeClass(o.animation);
}, 5000);
});
To increase the timeout you can do following:
return this.each(function(index, val) {
var o = options;
var obj = $(this);
var animation = o.animation;
obj.bind("click", function() {
obj.addClass(o.animation);
obj.addClass('vivify');
}, index * 2000);
});

How do I add a function to an element via jQuery?

I want to do something like this:
$('.dynamicHtmlForm').validate = function() {
return true;
}
$('.dynamicHtmlForm .saveButton').click(function() {
if (!$(this).closest('.dynamicHtmlForm').validate()) {
return false;
}
return true;
});
And then when I have a form of class dynamicHtmlForm, I want to be able to provide a custom validate() function:
$('#myDynamicHtmlForm').validate = function() {
// do some validation
if (there are errors) {
return false;
}
return true;
}
But I get this when I do this:
$(this).closest(".dynamicHtmlForm").validate is not a function
Is what I've described even possible? If so, what am I doing wrong?
Yes, it is technically possible. You will need to reference the element itself, however, and not the jQuery collection. This should work:
$('.dynamicHtmlForm').each(function (ix,o) {
o.validate = function() {
return true;
}
});
$('.dynamicHtmlForm .saveButton').click(function() {
if ($(this).closest('.dynamicHtmlForm')[0].validate()) {
return false;
}
return true;
}
jQuery.fn.validate = function(options) {
var defaults = {
validateOPtions1 : '',
validateOPtions2 : ''
};
var settings = $.extend({}, defaults, options);
return this.each(function() {
// you validation code goes here
});
};
$(document).ready(function() {
$('selector').click(function() {
$('some selector').validate();
// or if you used any options in your code that you
// want the user to enter. then you go :
$('some selector').validate({
validateOPtions1: 'value1',
validateOPtions2: 'value2'
});
});
});
You're not adding the function to the element, you're adding it to the jQuery wrapper around the element. Every time you pass a selector to jQuery, it will create a new wrapper for the found elements:
$('#myEl'); // gives a jQuery wrapper object
$('#myEl'); // creates another jQuery wrapper object
If you save the wrapped element to a variable and use that later, it would be a different story because you're accessing the saved jQuery wrapper object.
var dynamicHtmlForm = $('.dynamicHtmlForm');
dynamicHtmlForm.validate = function() {
return true;
}
$('.dynamicHtmlForm .saveButton').click(function() {
if (dynamicHtmlForm.validate()) {
return false;
}
return true;
}
You could also add the function directly to the element using
$('.dynamicHtmlForm')[0].validate = function () { return true; }
// and later...
if (!$(this).closest('.dynamicHtmlForm')[0].validate())
Or you could look at extending jQuery properly by writing a plugin.

Categories

Resources