Skip to content

Instantly share code, notes, and snippets.

@remy
Created April 24, 2012 22:45
Show Gist options
  • Save remy/2484402 to your computer and use it in GitHub Desktop.
Save remy/2484402 to your computer and use it in GitHub Desktop.
jquery.marquee.js
/**
* author Remy Sharp
* url http://remysharp.com/tag/marquee
*/
(function ($) {
$.fn.marquee = function (klass) {
var newMarquee = [],
last = this.length;
// works out the left or right hand reset position, based on scroll
// behavior, current direction and new direction
function getReset(newDir, marqueeRedux, marqueeState) {
var behavior = marqueeState.behavior, width = marqueeState.width, dir = marqueeState.dir;
var r = 0;
if (behavior == 'alternate') {
r = newDir == 1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : width;
} else if (behavior == 'slide') {
if (newDir == -1) {
r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] : width;
} else {
r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : 0;
}
} else {
r = newDir == -1 ? marqueeRedux[marqueeState.widthAxis] : 0;
}
return r;
}
// single "thread" animation
function animateMarquee() {
var i = newMarquee.length,
marqueeRedux = null,
$marqueeRedux = null,
marqueeState = {},
newMarqueeList = [],
hitedge = false;
while (i--) {
marqueeRedux = newMarquee[i];
$marqueeRedux = $(marqueeRedux);
marqueeState = $marqueeRedux.data('marqueeState');
if ($marqueeRedux.data('paused') !== true) {
// TODO read scrollamount, dir, behavior, loops and last from data
marqueeRedux[marqueeState.axis] += (marqueeState.scrollamount * marqueeState.dir);
// only true if it's hit the end
hitedge = marqueeState.dir == -1 ? marqueeRedux[marqueeState.axis] <= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState) : marqueeRedux[marqueeState.axis] >= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
if ((marqueeState.behavior == 'scroll' && marqueeState.last == marqueeRedux[marqueeState.axis]) || (marqueeState.behavior == 'alternate' && hitedge && marqueeState.last != -1) || (marqueeState.behavior == 'slide' && hitedge && marqueeState.last != -1)) {
if (marqueeState.behavior == 'alternate') {
marqueeState.dir *= -1; // flip
}
marqueeState.last = -1;
$marqueeRedux.trigger('stop');
marqueeState.loops--;
if (marqueeState.loops === 0) {
if (marqueeState.behavior != 'slide') {
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
} else {
// corrects the position
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
}
$marqueeRedux.trigger('end');
} else {
// keep this marquee going
newMarqueeList.push(marqueeRedux);
$marqueeRedux.trigger('start');
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
}
} else {
newMarqueeList.push(marqueeRedux);
}
marqueeState.last = marqueeRedux[marqueeState.axis];
// store updated state only if we ran an animation
$marqueeRedux.data('marqueeState', marqueeState);
} else {
// even though it's paused, keep it in the list
newMarqueeList.push(marqueeRedux);
}
}
newMarquee = newMarqueeList;
if (newMarquee.length) {
setTimeout(animateMarquee, 25);
}
}
// TODO consider whether using .html() in the wrapping process could lead to loosing predefined events...
this.each(function (i) {
var $marquee = $(this),
width = $marquee.attr('width') || $marquee.width(),
height = $marquee.attr('height') || $marquee.height(),
$marqueeRedux = $marquee.after('<div ' + (klass ? 'class="' + klass + '" ' : '') + 'style="display: block-inline; width: ' + width + 'px; height: ' + height + 'px; overflow: hidden;"><div style="float: left; white-space: nowrap;">' + $marquee.html() + '</div></div>').next(),
marqueeRedux = $marqueeRedux.get(0),
hitedge = 0,
direction = ($marquee.attr('direction') || 'left').toLowerCase(),
marqueeState = {
dir : /down|right/.test(direction) ? -1 : 1,
axis : /left|right/.test(direction) ? 'scrollLeft' : 'scrollTop',
widthAxis : /left|right/.test(direction) ? 'scrollWidth' : 'scrollHeight',
last : -1,
loops : $marquee.attr('loop') || -1,
scrollamount : $marquee.attr('scrollamount') || this.scrollAmount || 2,
behavior : ($marquee.attr('behavior') || 'scroll').toLowerCase(),
width : /left|right/.test(direction) ? width : height
};
// corrects a bug in Firefox - the default loops for slide is -1
if ($marquee.attr('loop') == -1 && marqueeState.behavior == 'slide') {
marqueeState.loops = 1;
}
$marquee.remove();
// add padding
if (/left|right/.test(direction)) {
$marqueeRedux.find('> div').css('padding', '0 ' + width + 'px');
} else {
$marqueeRedux.find('> div').css('padding', height + 'px 0');
}
// events
$marqueeRedux.bind('stop', function () {
$marqueeRedux.data('paused', true);
}).bind('pause', function () {
$marqueeRedux.data('paused', true);
}).bind('start', function () {
$marqueeRedux.data('paused', false);
}).bind('unpause', function () {
$marqueeRedux.data('paused', false);
}).data('marqueeState', marqueeState); // finally: store the state
// todo - rerender event allowing us to do an ajax hit and redraw the marquee
newMarquee.push(marqueeRedux);
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
$marqueeRedux.trigger('start');
// on the very last marquee, trigger the animation
if (i+1 == last) {
animateMarquee();
}
});
return $(newMarquee);
};
}(jQuery));
@ache
Copy link

ache commented Aug 10, 2012

FF 14.0.1 produce following error
Use of getAttributeNode() is deprecated. Use getAttribute() instead. @ jquery-1.7.2.min.js:2
when see $marquee.attr('loop'). FF don't think that 'loop' is attribute, but property, and latest jquery becomes very picky about .attr() vs. .prop(). usage. The fix is to change two $marquee.attr('loop') in the code to $marquee.prop('loop').

@slovette2
Copy link

WHere is the reference to the FOSS license for this java script?

@aamirafridi
Copy link

I tried something similar here https://github.com/aamirafridi/jQuery-Marquee

@kirokuan
Copy link

kirokuan commented Aug 7, 2013

I try the plugin with id name and some data-binding value,the plugin generate the new element and cause the original style gone

@floatplane
Copy link

Thanks for this! I tweaked it slightly to support % width and height on the marquee element: https://gist.github.com/floatplane/6226786

@JoeyMobileDev
Copy link

Isn't it display: inline-block instead of display: block-inline?

@Deks986
Copy link

Deks986 commented Jan 30, 2014

Hi! How can I change gap between elements. Example: I have 2 elements in row and I wanna let the first act like 3rd. So first go automatically after 2nd without waiting till everything finish?

Thanks

@yoopernc
Copy link

yoopernc commented Aug 1, 2014

I'm using this gistfile to smooth out my marquees, and it's working great!

What's needed to be able to handle the "onfinish" event? Thanks!

@HuanxinHu
Copy link

Hi!, the plugin is so cool, but there is a bug, the marquee is not responsive if the window width is changed. Could you solve this bug?

@parallaxinfotech
Copy link

@HuanxinHu
I just found myself looking for a solution to the same thing. I have a feeling a custom jquery function will have to be implemented to resize and change the padding.

@parallaxinfotech
Copy link

I have found a bug in this code. On line 100, you see the style "display: block-inline;". That is actually backwards. It should be "display: inline-block;"

@koppor
Copy link

koppor commented Jul 18, 2016

@aamirafridi - the updated URL is https://github.com/aamirafridi/jQuery.Marquee - currently 360 stars.

Even with CSS3, you still need to calculate the speed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment