Skip to content

Instantly share code, notes, and snippets.

@eric1234
Last active October 5, 2016 10:48
Show Gist options
  • Save eric1234/548690 to your computer and use it in GitHub Desktop.
Save eric1234/548690 to your computer and use it in GitHub Desktop.
<marquee> element lives! - Emulating the behavior of the old marquee element with CoffeeScript/JavaScript
# https://gist.github.com/eric1234/548690
class @Marquee
constructor: (@element, @options={}) ->
@clean @element
@stopped = true
# Set defaults
@options['duration'] or= 30
@options['wrapAround'] = true unless 'wrapAround' of @options
@options['direction'] or= 'forward'
# If everything fits don't bother with all this mess
return if this.element.scrollWidth == this.element.clientWidth
# Create wrapping container to scroll the marquee in.
# Adapted from Prototype's wrap method.
@container = document.createElement 'div'
@container.className = 'marquee-wrapper'
@element.parentNode.replaceChild @container, @element if @element.parentNode
@container.appendChild @element
# Move some of the relevant marquee styles to this wrapper.
@container.style.width = @element.clientWidth+'px'
@container.style.height = @element.clientHeight+'px'
@container.style.overflow = 'hidden'
# Keep in mind how big it was so we know when to reset
@originalWidth = @element.scrollWidth
# Make it look like it is wrapping around by duping content
if @options.wrapAround
trimmed = element.innerHTML.trim()
@element.innerHTML = trimmed + trimmed + trimmed
# Size restrictions/clipping moved to wrapper, remove from element
@element.style.width = @element.scrollWidth+'px'
@element.style.height = @element.scrollHeight+'px'
@element.style.overflow = 'auto'
@container.scrollLeft = @originalWidth if @options.wrapAround
# Will start the marquee moving.
#
# If no argument is given it will move until it gets to the end of the
# content. If wrapAround is enabled it will reset and do another move to the
# end of the content creating a never-ending scrolling of the content.
#
# If a distance is given then it will only move that far then stop.
#
# If a distance is given then the onFinish is called when the
# animation ends. If no distance is given then onFinish is called
# every time the animation resets.
move: (distance=null, onFinish=->) ->
@stopped = false
to = if distance?
distance
else
@originalWidth
ending_position = if @options.direction is 'forward'
@container.scrollLeft + to
else
@container.scrollLeft - to
if @options.wrapAround
if ending_position > 2 * @originalWidth
@container.scrollLeft -= @originalWidth
ending_position -= @originalWidth
if ending_position < 0
@container.scrollLeft += @originalWidth
ending_position += @originalWidth
start_position = @container.scrollLeft
callback = if @options.wrapAround && !distance
=>
onFinish()
unless @stopped
@container.scrollLeft = start_position
@animation = Marquee.tween @container, @options.duration, ending_position, callback
else
=>
@stopped = true
onFinish()
@animation = Marquee.tween @container, @options.duration, ending_position, callback
# Stops all animation so you can issue new move commands.
stop: ->
@stopped = true
Marquee.stop @animation if @animation
clean: (node) ->
to_remove = []
for c in node.childNodes
if c.nodeType is 8 || (c.nodeType is 3 && !/S/.test(c.nodeValue))
to_remove.push c
else if c.nodeType is 1
@clean c
for n in to_remove
n.parentNode.removeChild n
# To actually do the moving we farm this out to a callback. This way the actual
# movement can be handled by a library like morpheus which is designed as a
# single high-performance loop using constant speed animation and the
# requestAnimationFrame standard.
#
# This should be replaced by a function that actually does the work.
Marquee = @Marquee
Marquee.tween = (element, duration, target_scroll, oncomplete) ->
# By default implement a stupid "animation" that just move it without
# any in-between stages.
element.scrollLeft = target_scroll
oncomplete()
Marquee.stop = (animation) ->
# Adapted from
# http://stackoverflow.com/questions/498970/#answer-8522376
unless String::trim
String::trim = -> @replace /^\s+|\s+$/g,''
<!DOCTYPE html>
<html>
<head>
<style>
#test {
font-size: 3em;
width: 10em;
height: 1.5em;
overflow: hidden;
white-space: nowrap;
}
</style>
</head>
<div id="test">
The quick brown fox jumps over the lazy dog
</div>
<script src="marquee.js"></script>
<script src="morpheus.js"></script>
<script>
Marquee.tween = function(element, duration, end, onComplete) {
morpheus.tween(duration*1000, function(position) {
element.scrollLeft = position
}, onComplete, function(p) {return p}, element.scrollLeft, end)
}
marquee = new Marquee(document.getElementById('test'));
marquee.move();
</script>
</html>
@eric1234
Copy link
Author

Do you miss the old days of having text move across the screen in a jerky, hard to click manor? Sure IE still support marquee but the other browsers only support it in quirks mode. And how long before they drop it completely?

With the above script you can have all the joy of a marquee just like it was 1998. The implementation of this script was partially inspired by The Silky Smooth Marquee which suggested using scrollLeft to move the content instead of CSS to avoid causing the browser to repaint the content.

The real use case for this is to move a bunch of sponsor logos across the bottom of a non-profit site.

@eric1234
Copy link
Author

eric1234 commented Mar 8, 2012

Made the following updates:

  • Converted to coffeescript
  • Remove dependency on Prototype
  • Removed complicated/inaccurate duration param and replaced with frequency and moveBy. Might one day implement something more complicated that will really animate at a constant speed.
  • Removed play/pause and auto-pausing on hover and replaced with simple callback to provide more flexibility. The old behavior is easily emulated via the callback.
  • Added option to allow wrap-around to be enabled or disabled.
  • Added option so that it can scroll either direction.

@eric1234
Copy link
Author

Updated again. Replaced timeout animation with hook for real animation library to do tweening.

@eric1234
Copy link
Author

eric1234 commented Aug 8, 2012

Minor bug fixes and provided default "animation"

@leefish
Copy link

leefish commented Nov 10, 2012

Hi, I hope it is ok to ask here, but I was looking for a prototype marquee and this looked great - except I see you updated to coffeescript. Would you still have the old prototype code and be willing to repost it?

Thanks

@eric1234
Copy link
Author

The old code is still available under a previous revision. But the new code will work with prototype/scriptaculous. All you need to do is provide the Script.aculo.us tween effect in the callback and you can use the latest code.

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