I'm working on a jQuery plugin and already faced with interesting issue. Here is my code sample:
(function($){
var settings = {
'speed': 500
};
var methods = {
init: function(options){
settings = $.extend(options);
return this.each(function(){
$(this).bind('click.hideParagraph', methods.manage );
});
},
manage: function(){
if ($(this).hasClass("hidden")){
methods.show(this);
} else {
methods.hide(this);
}
},
show: function(item){
$(item).fadeIn(settings.speed, function(){});
},
hide: function(item){
$(item).fadeOut(settings.speed, function(){});
},
destroy: function(){
return this.each(function(){
$(this).unbind('click.hideParagraph');
});
}
};
$.fn.hideParagraph = 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.hideParagraph' );
}
};
})(jQuery);
The main problem is that functions fadeIn() and fadeOut() doesn't work this way (Error: f.speed is not a function in 'jquery.min.js'). Have anybody an idea why it happens?
And another question: is this the right way in implementing jQuery plugins? Could anyone suggest more convenient and flexible way?
Thanks.
Related
Hye,
Just a newbie trying to get something done and out of idea's I have a ul that is marqued from jquery function its marqueed left that means its sliding left what I want to do is to make it slide top
i Have made a working fiddle here
http://jsfiddle.net/arafays/wnXh8/
(function($)
{
var methods =
{
init : function( options )
{
return this.each(function()
{
var _this=$(this);
_this.data('marquee',options);
var _li=$('>li',_this);
_this.wrap('<div class="slide_container"></div>')
.height(_this.height())
.hover(function(){if($(this).data('marquee').stop){$(this).stop(true,false);}},
function(){if($(this).data('marquee').stop){$(this).marquee('slide');}})
.parent()
.css({position:'relative',overflow:'hidden','height':$('>li',_this).height()})
.find('>ul')
.css({width:screen.width*2,position:'relative'});
for(var i=0;i<Math.ceil((screen.width*3)/_this.width());++i)
{
_this.append(_li.clone());
}
_this.marquee('slide');});
},
slide:function()
{
var $this=this;
$this.animate({'left':$('>li',$this).width()*-1},
$this.data('marquee').duration,
'swing',
function()
{
$this.css('left',0).append($('>li:first',$this));
$this.delay($this.data('marquee').delay).marquee('slide');
}
);
}
};
$.fn.marquee = function(m)
{
var settings={
'delay':4000,
'duration':2000,
'stop':true
};
if(typeof m === 'object' || ! m)
{
if(m){
$.extend( settings, m );
}
return methods.init.apply( this, [settings] );
}
else
{
return methods[m].apply( this);
}
};
}
)( jQuery );
jQuery(document).ready(
function(){jQuery('.some ul').marquee({delay:3000});}
);
I just want to make it slide up instead of sliding left I tried making append top and doing some other some stuff but its making full ul slide up leaving blank space
Like this?
http://jsfiddle.net/wnXh8/5/
$this.animate({'top':$('>li',$this).height()*-1},
$this.data('marquee').duration,
'swing',
function()
{
$this.css('top',0).append($('>li:first',$this));
$this.delay($this.data('marquee').delay).marquee('slide');
}
);
I am trying to add some custom code to jQuery's beforeSend.
To do so I came up with this code :
(function( $ ){
$.ajax_overlay = function(settings, overlay_selector, target_selector, class_active, class_deactive) {
var $overlay = $(overlay_selector);
function overlay_before(user_beforesend){
if ( target_selector === undefined )
{
var $body = $('body');
$overlay.height($body.height()).width($body.width()).css("position","absolute");
}
else{
var $target= $(target_selector);
$overlay.height($target.height()).width($target.width()).css("position","absolute");
$target.css("position","relative").append($overlay);
}
if (typeof(class_active) == "string"){
$overlay.addClass(class_active)
}
if (typeof(class_deactive) == "string") {
$overlay.removeClass(class_deactive)
}
$overlay.css("display","block").animate(
{
opacity: 0.8
},
500);
if (typeof(user_beforesend) == "function"){
user_beforesend();
}
}
function overlay_complete(user_complete){
if (typeof(class_active) == "string"){
$overlay.removeClass(class_active);
}
if (typeof(class_deactive) == "string") {
$overlay.addClass(class_deactive);
}
$overlay.animate(
{
opacity: 0.0
},
500, function(){
$overlay.css("display","none");
});
if (typeof(user_complete) == "function"){
user_complete();
}
}
if (typeof(overlay_selector) == "string"){
settings["beforeSend"] = overlay_before(settings["beforeSend"]);
settings["complete"] = overlay_complete(settings["complete"]);
}
return jQuery.ajax(settings);
}
})( jQuery );
The main idea is to add an overlay on each ajax request using this plugin.
The issue I am facing is that I can't keep the original behaviour of the beforeSend parameter as it expects an xhr and settings paramters.
How can I get a proper xhr to feed to my beforeSend function ?
EDIT:
Found some interesting links/answers related to this onee
Adding code to a javascript function programmatically
Overriding a JavaScript function while referencing the original
Having your overlay_before return a function with expected parameters, and propagate these parameters to the user_beforesend call, should do the trick :
function overlay_before(user_beforesend){
return function(xhr, settings)
{
if ( target_selector === undefined )
{
var $body = $('body');
$overlay.height($body.height()).width($body.width()).css("position","absolute");
}
else{
var $target= $(target_selector);
$overlay.height($target.height()).width($target.width()).css("position","absolute");
$target.css("position","relative").append($overlay);
}
if (typeof(class_active) == "string"){
$overlay.addClass(class_active)
}
if (typeof(class_deactive) == "string") {
$overlay.removeClass(class_deactive)
}
$overlay.css("display","block").animate(
{
opacity: 0.8
},
500);
if (typeof(user_beforesend) == "function"){
user_beforesend(xhr,settings);
}
}
}
Have you tried var xhr = new XmlHttpRequest();? Or are you looking for something more/different?
I'm aware there's plenty of JQuery slideshows out there but I'm coding my own and have come across an obstacle I've not found an answer to specifically.
Background: An automated JQuery Slideshow plugin with pause/resume on hover/out
Problem: How to pause the slideshow on mousenter, and then finish the remaining time period of the setInterval on mouseleave instead of firing the setInterval again.
Solution: I think the only way to do this would be to use a self-invoking method with a delay attached and a stop() to handle the mouse behaviour which would allow the paused animation to resume for its proper remainder rather than firing the method again?
What are your thoughts?
Here's the code:
(function( $ ){
var methods = {
init: function() {
var cycle = window.setInterval(methods.automate, 1000);
$('.slider_control').on('click', 'span', function(){
methods.automate( $(this).attr('slideNumber') );
});
$('.slider_master_container')
.on({
'mouseenter': function() {
clearInterval(cycle);
},
'mouseleave': function() {
cycle = window.setInterval(methods.automate, 1000);
}
});
},
automate: function( el ) {
var $active = $('.slide.active'),
$next = el ? $('.slide[slideNumber=' + el) : $active.nextOrFirst();
$next.css('z-index', 2);
$active.fadeOut(1500, function() {
$active.css('z-index', 1).show().removeClass('active');
$next.css('z-index', 3).addClass('active');
});
}
};
$.fn.smartSlider = 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.smartSlider' );
}
};
})( jQuery );
I'm building my first ever jQuery plugin (it's just a simple experiment). Here's what I have so far:
(function($){
$.fn.extend({
auchieFader: function(options) {
var defaults = {
mask: '',
topImg : '',
}
var options = $.extend(defaults, options);
return this.each(function() {
var o = options;
var obj = $(this);
var masker = $(o.mask, obj);
masker.hover(function () {
$(o.topImg).stop().animate({
"opacity": "0"
}, "slow");
}, function () {
$(o.topImg).stop().animate({
"opacity": "1"
}, "slow");
});
});
}
});
})(jQuery);
I'm then calling the plugin using:
$('.fader').auchieFader({mask: ".mask", topImg: ".top"});
If I then add another request say:
$('.fader2').auchieFader({mask: ".mask", topImg: ".top"});
Then no matter what instance of my 2 faders I hover both of them will trigger. I know this is because my mask mask and topImg options have the same class - but how can I modify the plugin to allow for these items to have the same class? I know it's probably something really simple, but I'm still finding my way with jQuery and Javascript in general. Any other tips on improving my code would also be greatly appreciated!
Cheers,
Chris
You seem to already have the answer to your question in the code. For the masker you wrote this:
var masker = $(o.mask, obj);
Which scopes the class in o.mask inside of the dom element obj
I think you just need to do the same thing for o.topImg.
Try changing
masker.hover(function () {
$(o.topImg)
into
masker.hover(function () {
$(o.topImg, obj)
try this:
(function($){
$.fn.extend({
auchieFader: function(options) {
var
// defaults options
defaults = {
mask: null,
topImg: null
},
// extend options in defaults
o = $.extend(defaults, options);
if (!o.mask || !o.topImg) return this;
return this.each(function() {
var
masker = $(o.mask, this),
topImg = $(o.topImg, this);
if (masker.length == 0 || topImg.length == 0) return;
masker.hover(
function () { topImg.stop().animate({ "opacity": "0" }, "slow"); },
function () { topImg.stop().animate({ "opacity": "1" }, "slow"); }
);
});
};
});
})(jQuery);
I need to wait for document readyness in my JavaScript, to insert a div at the bottom of the body.
I want to:
make this JavaScript file as small as possible (compile it down to < 1kb if possible)
inline the code that provides the document readyness in a closure (without exporting it)
Inlining the whole jQuery source in my file would be too big, so I'm looking for other methods. window.onload would work, but I specifically want document readyness, and not wait for the window.onload event.
Does anyone know a JS snippet that can do this? Or should I just copy part of jQuery's source?
EDIT:
I managed to crawl the jQuery source and put together with the following snippet:
var ready = (function () {
var ready_event_fired = false;
var ready_event_listener = function (fn) {
// Create an idempotent version of the 'fn' function
var idempotent_fn = function () {
if (ready_event_fired) {
return;
}
ready_event_fired = true;
return fn();
}
// The DOM ready check for Internet Explorer
var do_scroll_check = function () {
if (ready_event_fired) {
return;
}
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
try {
document.documentElement.doScroll('left');
} catch(e) {
setTimeout(do_scroll_check, 1);
return;
}
// Execute any waiting functions
return idempotent_fn();
}
// If the browser ready event has already occured
if (document.readyState === "complete") {
return idempotent_fn()
}
// Mozilla, Opera and webkit nightlies currently support this event
if (document.addEventListener) {
// Use the handy event callback
document.addEventListener("DOMContentLoaded", idempotent_fn, false);
// A fallback to window.onload, that will always work
window.addEventListener("load", idempotent_fn, false);
// If IE event model is used
} else if (document.attachEvent) {
// ensure firing before onload; maybe late but safe also for iframes
document.attachEvent("onreadystatechange", idempotent_fn);
// A fallback to window.onload, that will always work
window.attachEvent("onload", idempotent_fn);
// If IE and not a frame: continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch (e) {}
if (document.documentElement.doScroll && toplevel) {
return do_scroll_check();
}
}
};
return ready_event_listener;
})();
// TEST
var ready_1 = function () {
alert("ready 1");
};
var ready_2 = function () {
alert("ready 2");
};
ready(function () {
ready_1();
ready_2();
});
Thank you very much for helping me find this in the jQuery source. I can now put all this in a closure and do my work without exporting any functions and polluting the global scope.
One option would be to just get the core.js jQuery file from github.
You could probably slim it down quite a bit for code you don't need. Then run it through YUI compressor, and it should be pretty small.
http://github.com/jquery/jquery/blob/1.4.2/src/core.js (jQuery core)
http://yui.2clics.net/ (YUI compressor online)
I tried it, and this code worked properly:
$(function() {
var newDiv = document.createElement('div');
document.getElementsByTagName('body')[0].appendChild(newDiv);
});
Update: This was as small as I got it. It is entirely from jQuery and is around 1,278 bytes (compressed). Should get smaller when you gzip.
Only difference is that you need to call it like:
$.fn.ready(function() {
// your code
});
YUI Compressed:
(function(){var e=function(i,j){},c=window.jQuery,h=window.$,d,g=false,f=[],b;e.fn={ready:function(i){e.bindReady();if(e.isReady){i.call(document,e)}else{if(f){f.push(i)}}return this}};e.isReady=false;e.ready=function(){if(!e.isReady){if(!document.body){return setTimeout(e.ready,13)}e.isReady=true;if(f){var k,j=0;while((k=f[j++])){k.call(document,e)}f=null}if(e.fn.triggerHandler){e(document).triggerHandler("ready")}}};e.bindReady=function(){if(g){return}g=true;if(document.readyState==="complete"){return e.ready()}if(document.addEventListener){document.addEventListener("DOMContentLoaded",b,false);window.addEventListener("load",e.ready,false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",b);window.attachEvent("onload",e.ready);var i=false;try{i=window.frameElement==null}catch(j){}if(document.documentElement.doScroll&&i){a()}}}};d=e(document);if(document.addEventListener){b=function(){document.removeEventListener("DOMContentLoaded",b,false);e.ready()}}else{if(document.attachEvent){b=function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",b);e.ready()}}}}function a(){if(e.isReady){return}try{document.documentElement.doScroll("left")}catch(i){setTimeout(a,1);return}e.ready()}window.jQuery=window.$=e})();
Full source (again, this is jQuery code):
(function() {
var jQuery = function( selector, context ) {
},
_jQuery = window.jQuery,
_$ = window.$,
rootjQuery,
readyBound = false,
readyList = [],
DOMContentLoaded;
jQuery.fn = {
ready: function( fn ) {
jQuery.bindReady();
if ( jQuery.isReady ) {
fn.call( document, jQuery );
} else if ( readyList ) {
readyList.push( fn );
}
return this;
}
};
jQuery.isReady = false;
jQuery.ready = function() {
if ( !jQuery.isReady ) {
if ( !document.body ) {
return setTimeout( jQuery.ready, 13 );
}
jQuery.isReady = true;
if ( readyList ) {
var fn, i = 0;
while ( (fn = readyList[ i++ ]) ) {
fn.call( document, jQuery );
}
readyList = null;
}
if ( jQuery.fn.triggerHandler ) {
jQuery( document ).triggerHandler( "ready" );
}
}
};
jQuery.bindReady = function() {
if ( readyBound ) {
return;
}
readyBound = true;
if ( document.readyState === "complete" ) {
return jQuery.ready();
}
if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
window.addEventListener( "load", jQuery.ready, false );
} else if ( document.attachEvent ) {
document.attachEvent("onreadystatechange", DOMContentLoaded);
window.attachEvent( "onload", jQuery.ready );
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
};
rootjQuery = jQuery(document);
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
jQuery.ready();
};
} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
jQuery.ready();
}
};
}
function doScrollCheck() {
if ( jQuery.isReady ) {
return;
}
try {
document.documentElement.doScroll("left");
} catch(e) {
setTimeout( doScrollCheck, 1 );
return;
}
jQuery.ready();
}
window.jQuery = window.$ = jQuery;
})();
I'm sure there are more bytes that could be removed.
Don't forget:
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
There are several implementations for "DOMReady" functions but most that I can find seem a bit dated, so I don't know how they will behave with IE8 and such.
I would recommend using jQuery's ready() as I think it promises the most cross-browser compatibility. I'm not an expert in jQuery's source code, but this seems to be the right spot (lines 812-845 or search for function bindReady).
You can start with script: http://snipplr.com/view/6029/domreadyjs/, not optimized (but work) for latest Safari though (e.g. use timer instead of supported DOMContentLoaded).