multiple instances of my jQuery plugin on one page - javascript

I'm writing my first jQuery plugin using the jQuery UI template and I'm trying to instantiate two instances of the same plugin - but with different options.
I could do with some help please!
Javascript:
(function ($) {
// **********************************
// ***** Start: Private Members *****
var pluginName = 'onFancyLinks',
version = '1.0';
// ***** Fin: Private Members *****
// ********************************
// *********************************
// ***** Start: Public Methods *****
var methods = {
init: function (options) {
//"this" is a jquery object on which this plugin has been invoked.
return this.each(function (index) {
var $this = $(this);
var data = $this.data(pluginName);
// If the plugin hasn't been initialized yet
if (!data) {
var settings = {
lineColor: '#fff',
lineWidth: 1,
wrapperClass: 'fancy-link',
linesClass: 'line',
transDuration: '0.7s'
};
if (options) {
$.extend(true, settings, options);
}
$this.data(pluginName, {
target: $this,
settings: settings
});
}
});
},
wrap: function () {
return this.each(function () {
var $this = $(this),
data = $this.data(pluginName),
opts = data.settings,
//wrapping div
wrapper = '<div class="' + opts.wrapperClass + '"></div>',
lines = {
top: '<div class="' + opts.linesClass + ' line-top"> </div>',
right: '<div class="' + opts.linesClass + ' line-right"> </div>',
bottom: '<div class="' + opts.linesClass + ' line-bottom"> </div>',
left: '<div class="' + opts.linesClass + ' line-left"> </div>'
};
$this.wrap(wrapper);
$('.' + opts.wrapperClass).append(lines.top, lines.right, lines.bottom, lines.left);
//setup transition duration of lines animation
$('.' + opts.wrapperClass + ' .' + opts.linesClass).css({
'transition-duration': opts.transDuration,
backgroundColor: opts.lineColor,
borderWidth: opts.lineWidth
});
});
}
};
// ***** Fin: Public Methods *****
// *******************************
// *****************************
// ***** Start: Supervisor *****
$.fn[pluginName] = 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 in jQuery.' + pluginName);
}
};
// ***** Fin: Supervisor *****
// ***************************
})(jQuery);
$(function() {
var v1 = $('#flink2').onFancyLinks({
lineColor: '#f00',
lineWidth: 2,
transDuration: '.4s'
});
var v2 = $('#flink').onFancyLinks({
lineColor: '#ff0',
lineWidth: 1,
transDuration: '.7s'
});
v1.onFancyLinks('wrap');
v2.onFancyLinks('wrap');
});
HTML:
<a id="flink2" href="http://www.google.co.uk">View Google</a>
<a id="flink" href="http://www.visarc.co.uk">View Visarc Site</a>
Here's my fiddle: http://jsfiddle.net/owennicol/xhuxk/14/
I'm sure it's something simple that I'm missing...

Very nice plugin, but there's a subtle logical error in it. Here's the critical part of the patched version:
var $wrapper = $this.wrap(wrapper).parent();
$wrapper.append(lines.top, lines.right, lines.bottom, lines.left);
//setup transition duration of lines animation
$wrapper.find('.' + opts.linesClass).css({
'transition-duration': opts.transDuration,
backgroundColor: opts.lineColor,
borderWidth: opts.lineWidth
});
See? You no longer look through the whole DOM for $('.' + opts.wrapperClass) and $('.' + opts.wrapperClass + ' .' + opts.linesClass) respectively - but instead scan only within the wrapper created for a specific jQuery element (processed by the plugin).
And that's exactly what was wrong in the original version: even though options were set up correctly (that's easy to check - just add console.log(options) into wrap method), the css was applied throughout the whole DOM for these line elements.

Related

How to set an element property from within a callback function

I have this "service" element where I would like to set the property "bookmarks" with the function getTree, which takes a callback function.
My problem is that I don't see how I could reach the property from within the callback function where "this" is undefined!!
<dom-module id="...">
<style>
:host {
display: none;
}
</style>
<script>
Polymer({
is: "bookmark-service",
properties: {
bookmarks: {
type: Array,
value: function() { return [{title:"init"}]; }
}
},
created: function() {
chrome.bookmarks.getTree(
function(bookmarkTreeNodes) {
this.bookmarks = bookmarkTreeNodes;
console.log(this.localName + '#' + this.id + ' in getTree.');
} );
console.log(this.localName + '#' + this.id + ' was created');
console.log("Bookmark: " + this.bookmarks[0].title + '.');
},
...
You could save a reference for this before calling getTree:
var that = this;
chrome.bookmarks.getTree(function(bookmarkTreeNodes) {
that.bookmarks = bookmarkTreeNodes;
console.log(that.localName + '#' + that.id + ' in getTree.');
});
You can use bind to set this in your callback function.
chrome.bookmarks.getTree(
function(bookmarkTreeNodes) {
this.bookmarks = bookmarkTreeNodes;
console.log(this.localName + '#' + this.id + ' in getTree.');
}.bind(this) );
That was a part of my problem and I prefer not to use "bind" which I fear may have side effects with this and looks more complicated.
But another problem, was the asynchronous nature of getTree. For this, I had to add an observer.
Also, the properties doesn't even exist in "created" phase, I had to use "ready"
So here is the almost final result:
properties: {
bookmarks: {
type: Array,
value: function() { return [{title:"init"}]; },
observer: 'bookready'
}
},
bookready: function(){
console.log("Bookmark ready: " + this.bookmarks[0].title + '.');
},
ready: function() {
var self = this;
chrome.bookmarks.getTree(
function(bookmarkTreeNodes) {
self.bookmarks = bookmarkTreeNodes[0].children;
}
);
console.log(this.localName + '#' + this.id + ' was readied');
console.log("Bookmark: " + this.bookmarks[0].title + '.');
},

Javascript Parameters Function on Many Events

I'm trying to implement hammer.js to swipe pages (like a book) and I did it. The problem is that this works
var idHammer1 = document.getElementById("pageHoja1")
//var hammertime = new Hammer(myElement, hammerOptionsPan);
var objHammer1 = new Hammer(idHammer1);
objHammer1.on('panleft panright', function(ev)
{
//DBLog("obj1 - gSceneActual Antes: " + gSceneActual + " // X: " + ev.center.x + " Y: " + ev.center.y);
if (ev.type==='panleft')
{
if (!(gSceneActual===2))
{
gSceneActual = 2;
$(":mobile-pagecontainer").pagecontainer("change", "#pageHoja2", { transition: "slide", reverse: false});
}
}
else if (ev.type==='panright')
{
}
});
but this doesn't:
var fSwipe1 = function(ev)
{
//DBLog("obj1 - gSceneActual Antes: " + gSceneActual + " // X: " + ev.center.x + " Y: " + ev.center.y);
if (ev.type==='panleft')
{
if (!(gSceneActual===2))
{
gSceneActual = 2;
$(":mobile-pagecontainer").pagecontainer("change", "#pageHoja2", { transition: "slide", reverse: false});
}
}
else if (ev.type==='panright')
{
}
}
var idHammer1 = document.getElementById("pageHoja1")
//var hammertime = new Hammer(myElement, hammerOptionsPan);
var objHammer1 = new Hammer(idHammer1);
objHammer1.on('panleft panright', fSwipe1(ev))
and this also don't work
function fSwipe1(ev)
{
//DBLog("obj1 - gSceneActual Antes: " + gSceneActual + " // X: " + ev.center.x + " Y: " + ev.center.y);
if (ev.type==='panleft')
{
if (!(gSceneActual===2))
{
gSceneActual = 2;
$(":mobile-pagecontainer").pagecontainer("change", "#pageHoja2", { transition: "slide", reverse: false});
}
}
else if (ev.type==='panright')
{
}
}
and since I need to add this event to many pages (variable #) I cant hardcode it... How can I make it variable inside a cycle?
Thanks!
Ah, without knowing the extent of the errors, I do see:
objHammer1.on('panleft panright', fSwipe1(ev));
Here, you are rendering the function automatically, but what you actually want is to use a closure so that the function does not get rendered until the event gets hit. I'm not sure what ev represents, but if it is the event object, then this should work:
objHammer1.on('panleft panright', fSwipe1);
Where all you are doing is passing in the function that you want to be the callback and the even will automatically call this function and pass the event object as the first parameter.
A few other things that I notice:
make sure that you include the javascript library for Hammer
Make sure that gSceneActual is defined before it is evaluated at gSceneActual===2
Make sure that jQuery library is included

Aftershow in fancybox jQuery

I had the same problem this person: ShareThis plugin not working in FancyBox title But now I have gotten it work, except the
afterShow: function(){
stButtons.locateElements();
}
Where in this code should I put it? I've tried many places, but it just says error. It say's I am placing it wrong. Where should I place it in the script below?
<script type="text/javascript">
$(document).ready(function(){
$(".fancybox").fancybox({
beforeShow: function() {
var caption = $(this.element).data("caption") ? $(this.element).data("caption") : "";
this.title = this.title ? this.title + buildShareThis(this.href) + caption : buildShareThis(this.href) + caption;
if (this.title) { ''
// New line
this.title += '<br />';
}
},
nextEffect : 'fade',
prevEffect : 'fade',
padding : 0,
margin : [15, 15, 50, 15],
afterLoad : addLinks,
beforeClose : removeLinks
});
function buildShareThis(url){
var customShareThis = "<div class='share'>"; // class for styling maybe
customShareThis += "<span class='st_facebook_hcount' displayText='Facebook' st_url='"+url+"'></span> ";
customShareThis += "<span class='st_twitter_hcount' displayText='Tweet' st_url='"+url+"'></span>";
customShareThis += "<span class='st_pinterest_hcount' displayText='Pinterest' st_url='"+url+"' st_img='"+url+"' ></span>";
customShareThis += "<span class='st_tumblr_hcount' displayText='Tumblr' st_url='"+url+"'></span>";
customShareThis += "</div>";
return customShareThis;
}
function addLinks() {
var list = $("#links");
if (!list.length) {
list = $('<ul id="links">');
for (var i = 0; i < this.group.length; i++) {
$('<li data-index="' + i + '"><label></label></li>').click(function() { $.fancybox.jumpto( $(this).data('index'));}).appendTo( list );
}
list.appendTo( 'body' );
}
list.find('li').removeClass('active').eq( this.index ).addClass('active');
}
function removeLinks() {
$("#links").remove();
}
});
</script>
You can set all fancybox API options, including fancybox callbacks (afterLoad, afterShow, beforeClose, etc.) like :
$(".fancybox").fancybox({
nextEffect: 'fade',
prevEffect: 'fade',
padding: 0,
margin: [15, 15, 50, 15],
afterLoad: addLinks,
afterShow: function () {
stButtons.locateElements();
},
beforeClose: removeLinks
});
Assuming you have properly defined your addLinks and removeLinks functions somewhere else in your script.

Jquery plugin using second instance's options

I have written a Jquery Pagination plugin that works great with just one instance of the plugin. When I try to use two instances, the first instance ignores its given options and uses the second instance's options. I know this because the two sections both start out with the defined items per page, but when you navigate to another 'page' in the pagination, it reverts to the second instance's itemsPerPage - 2.
My guess is the second time this plugin is called, it is overwriting $.pagination's options, so when either pagination goes to a new page, it uses the overwritten options.
Here's the plugin:
/* Jquery Pagination */
(function($){
$.pagination = {
defaultOptions : {
itemsPerPage : 5,
startPage : 1,
showNextPrev : true,
navigationPosition : 'before',
paginationClass : 'pagination',
paginationItemClass : 'paginationItem',
paginationItemActiveClass : 'active',
nextClass : 'next',
nextText : 'Next',
prevClass : 'prev',
prevText : 'Prev',
}
}
$.fn.extend({
pagination : function(newOptions){
var options = $.extend($.pagination.defaultOptions, newOptions),
itemsToPaginate = $(this),
itemsToPaginateContainer = itemsToPaginate.eq(0).parent(),
paginationWrapper = "<div class='" + options.paginationClass + "'></div>",
paginationControls = '',
pagination,
numberOfPages,
showPage = function(goToPage){
var page = (typeof goToPage === 'number') ? goToPage : goToPage.attr('href').replace('#page', ''),
itemRangeEnd = page * options.itemsPerPage
itemRangeStart = itemRangeEnd - options.itemsPerPage;
$( '.' + options.paginationItemClass, pagination).removeClass(options.paginationItemActiveClass);
if (typeof goToPage === 'number')
pagination.find('.' + options.paginationItemClass).eq(goToPage-1).addClass(options.paginationItemActiveClass);
else
goToPage.addClass(options.paginationItemActiveClass);
itemsToPaginate.hide().slice(itemRangeStart, itemRangeEnd).show();
},
createPagination = (function(){
// Add pagination element to DOM
switch(options.navigationPosition.toLowerCase()){
/*
// TODO: Create ability to insert pagination after or before & after
case 'both':
itemsToPaginateContainer.before(paginationWrapper);
itemsToPaginateContainer.after(paginationWrapper);
break;
case 'after':
itemsToPaginateContainer.after(paginationWrapper);
break;
*/
default:
itemsToPaginateContainer.before(paginationWrapper);
break;
}
// Selecting pagination element
pagination = itemsToPaginateContainer.siblings('.' + options.paginationClass);
// Count how many pages to make
numberOfPages = Math.ceil( itemsToPaginate.length / options.itemsPerPage );
// Insert controls into pagination element
if(options.showNextPrev) paginationControls += "<a href='#' class='" + options.prevClass + "'>" + options.prevText + "</a>";
for (var i = 1; i <= numberOfPages; i++) {
paginationControls += "<a href='#page" + i + "' class='" + options.paginationItemClass + "'>" + i + "</a>";
}
if(options.showNextPrev) paginationControls += "<a href='#' class='" + options.nextClass + "'>" + options.nextText + "</a>";
(numberOfPages !== 1) ? pagination.html(paginationControls) : pagination.remove() ;
}()),
bindUIEvents = (function(){
pagination.find('.' + options.paginationItemClass + ':not(.' + options.nextClass + '):not(.' + options.prevClass + ')').on('click', function(e){
e.preventDefault();
showPage( $(this) );
});
pagination.find('.' + options.prevClass).on('click', function(){
var prevPageIdx = pagination.find('.' + options.paginationItemActiveClass).index() - 1;
// console.log(prevPageIdx);
if(prevPageIdx < 1)
showPage(numberOfPages);
else
showPage(prevPageIdx);
});
pagination.find('.' + options.nextClass).on('click', function(){
var nextPageIdx = pagination.find('.' + options.paginationItemActiveClass).index() + 1;
if(nextPageIdx > numberOfPages)
showPage(1);
else
showPage(nextPageIdx);
});
}());
showPage(options.startPage);
return this;
}
});
})(jQuery);
JSFiddle
Any idea why each instance of this plugin doesn't just use its own options? How would I need to structure a plugin to encapsulate and protect their own options? Thanks!
Change
var options = $.extend($.pagination.defaultOptions, newOptions),
To
var options = $.extend({}, $.pagination.defaultOptions, newOptions),
Demo
Reason is you are providing the target as defaultOption while using the syntax jQuery.extend( target [, object1 ] [, objectN ]

How to name and reuse a javascript function

I have the following bit of jQuery code that i want to reuse by calling it from other parts of my jQuery code. How would i do that?
$(document).ready(function() {
$('#share_mention').charcount({
maxLength: 140,
preventOverage: false
});
$('.countable').bind('update', function(evt, length, remaining) {
var message = 'id=' + $(evt.target).attr('id') + ', length=' + length + ', remaining=' + remaining;
});
});
There are many ways to skin this cat but here is an approach.
var yourNameSpace = {};
yourNameSpace.YourFunction = function(){
$('#share_mention').charcount({
maxLength: 140,
preventOverage: false
});
$('.countable').bind('update', function(evt, length, remaining) {
var message = 'id=' + $(evt.target).attr('id') + ', length=' + length + ', remaining=' + remaining;
});
}
$(document).ready(function() {
yourNameSpace.YourFunction()
});
First things first there is no such thing as a jQuery function. It's a javascript function.
The event is implicitly passed to the callback function.
function countCats (event) {}
$('.cats').on('click', countCats);

Categories