I've had a go at writing my first plugin. It is a simple jQuery plugin that reorders the DOM based on screen width. If the plugin is used on a single selector, like $("#box3").domorder(); it works as expected.
However, if multiple selectors are used to call the function, or the function is called multiple times, the function only works once, on the first selector it comes to.
I have the each(function(){}) iterating over each selector, but I'm obviously missing something.
jsFiddle
(function($) {
"use strict";
$.fn.domorder = function(options) {
var settings = {
breakpoint : 960,
targetContainer : $(this).parent(),
targetPosition : "start"
};
if (options) {
$.extend(settings, options);
}
return this.each(function(i, el) {
/* remember selector's original order position */
var originalLocation = {};
if ($(el).prev().length) {
/* not originally first child */
originalLocation.prev = $(el).prev()[0];
} else {
/* originally a first child */
originalLocation.parent = $(el).parent()[0];
}
var initiatedomorder = function() {
var winW = $(window).width();
if (winW < settings.breakpoint && !$("body").hasClass("domorder-reordered")) {
/* dom the order of the item */
if (settings.targetPosition === "start") {
$(el).prependTo(settings.targetContainer[0]);
} else {
$(el).appendTo(settings.targetContainer[0]);
}
$("body").addClass("domorder-reordered");
} else if (winW >= settings.breakpoint && $("body").hasClass("domorder-reordered")) {
/* return the reordered item back into the orignal flow */
if (originalLocation.parent) {
/* element was a first child */
$(originalLocation.parent).prepend(el);
} else {
/* element was not a first child */
/* add a line break to preserve inline-block spacing */
$(originalLocation.prev).after(el).after("\n");
}
$("body").removeClass("domorder-reordered");
}
};
initiatedomorder();
$(window).resize(function() {
initiatedomorder();
});
});
};
}(jQuery));
$("body").addClass("domorder-reordered"); cause the problem since only the first iteration will do the ordering.
maybe you should change the $("body") to something like $(el.parentNode).
check this jsfiddle
You are adding the class to the body on the first iteration of the loop. so in the second iteration body has the class and wot go inside the ordering code. you have to rethink about the location of addClass and removeClass
and put the following code outside the each
$(window).resize(function() {
initiatedomorder();
});
Related
I'm using the Microsoft Translation Widget, which I'd like to use to automatically translate a webpage without user interaction.
The problem is, I can't get rid of the widget that keeps popping up or hide it on document.ready because the CSS and JS get loaded from Microsoft's own script in the widget!
Does anyone know a way around this? I've looked everywhere and cannot find a solutuion for this.
Whoa, after some time playing around with that, I've finally achieved what you want.
It's kindda ugly, because of some needed workarounds, but it works, take a look at the fiddle.
The steps were:
Firstly, we must override the default addEventListener behavior:
var addEvent = EventTarget.prototype.addEventListener;
var events = [];
EventTarget.prototype.addEventListener = function(type, listener) {
addEvent.apply(this, [].slice.call(arguments));
events.push({
element: this,
type: type,
listener: listener
});
}
Then, we create a helper function removeEvents. It removes all the event listeners of an element.
var removeEvents = function(el, type) {
var elEvents = events.filter(function(ev) {
return ev.element === el && (type ? ev.type === type : true);
});
for (var i = 0; i < elEvents.length; i++) {
el.removeEventListener(elEvents[i].type, elEvents[i].listener);
}
}
When creating the script tag, in the way Microsoft says:
var s = d.createElement('script');
s.type = 'text/javascript';
s.charset = 'UTF-8';
s.src = ((location && location.href && location.href.indexOf('https') == 0) ? 'https://ssl.microsofttranslator.com' : 'http://www.microsofttranslator.com') + '/ajax/v3/WidgetV3.ashx?siteData=ueOIGRSKkd965FeEGM5JtQ**&ctf=True&ui=true&settings=Manual&from=';
var p = d.getElementsByTagName('head')[0] || d.dElement;
p.insertBefore(s, p.firstChild);
We must add a load event listener to that script, and the code below is fully commented:
s.addEventListener('load', function() {
// when someone changes the translation, the plugin calls the method TranslateArray
// then, we save the original method in a variable, and we override it
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
// we call the original method
translate.apply(this, [].slice.call(arguments));
// since the translation is not immediately available
// and we don't have control when it will be
// I've created a helper function to wait for it
waitForTranslation(function() {
// as soon as it is available
// we get all the elements with an attribute lang
[].forEach.call(d.querySelectorAll('[lang]'), function(item, i) {
// and we remove all the mouseover event listeners of them
removeEvents(item, 'mouseover');
});
});
}
// this is the helper function which waits for the translation
function waitForTranslation(cb) {
// since we don't have control over the translation callback
// the workaround was to see if the Translating label is visible
// we keep calling the function, until it's hidden again
// and then we call our callback
var visible = d.getElementById('FloaterProgressBar').style.visibility;
if (visible === 'visible') {
setTimeout(function() {
waitForTranslation(cb);
}, 0);
return;
}
cb();
}
});
Update 1
After re-reading your question, it seems you want to hide all the widgets at all.
So, you must add the following code as soon as the translation is got:
waitForTranslation(function() {
document.getElementById('MicrosoftTranslatorWidget').style.display = 'none';
document.getElementById('WidgetLauncher').style.display = 'none';
document.getElementById('LauncherTranslatePhrase').style.display = 'none';
document.getElementById('TranslateSpan').style.display = 'none';
document.getElementById('LauncherLogo').style.display = 'none';
document.getElementById('WidgetFloaterPanels').style.display = 'none';
// rest of the code
});
I've created another fiddle for you, showing that new behavior.
Update 2
You can prevent the widget showing at all by adding the following CSS code:
#MicrosoftTranslatorWidget, #WidgetLauncher, #LauncherTranslatePhrase, #TranslateSpan, #LauncherLogo, #WidgetFloaterPanels {
opacity: 0!important;
}
And you can even prevent the before-translated text being showed, by hiding the document.body by default, and then showing it when the page is fully translated:
(function(w, d) {
document.body.style.display = 'none';
/* (...) */
s.addEventListener('load', function() {
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
translate.apply(this, [].slice.call(arguments));
waitForTranslation(function() {
/* (...) */
document.body.style.display = 'block';
});
}
});
});
Take a look at the final fiddle I've created.
For me, this was the solution:
on your < style > section add this class
.LTRStyle { display: none !important }
Also, if you are invoking the translation widget this way:
Microsoft.Translator.Widget.Translate('en', lang, null, null, TranslationDone, null, 3000);
then add this to your callback (in this example is TranslationDone) function:
function TranslationDone() {
Microsoft.Translator.Widget.domTranslator.showHighlight = false;
Microsoft.Translator.Widget.domTranslator.showTooltips = false;
document.getElementById('WidgetFloaterPanels').style.display = 'none';
};
So I have the following code I have written to build a carousel in JavaScript using Hammer.js and jQuery:
var hCarousel = {
container: false,
panes: false,
pane_width: 0,
pane_count: 0,
current_pane: 0,
build: function( element ) {
hCarousel.container = $(element).find('.hcarousel-inner-container');
hCarousel.panes = $(hCarousel.container).find('> .section');
hCarousel.pane_width = 0;
hCarousel.pane_count = hCarousel.panes.length;
hCarousel.current_pane = 0;
hCarousel.setPaneDimensions( element );
$(window).on('load resize orientationchange', function() {
hCarousel.setPaneDimensions( element );
});
$(element).hammer({ drag_lock_to_axis: true })
.on('release dragleft dragright swipeleft swiperight', hCarousel.handleHammer);
},
setPaneDimensions: function( element ){
hCarousel.pane_width = $(element).width();
hCarousel.panes.each(function() {
$(this).width(hCarousel.pane_width);
});
hCarousel.container.width(hCarousel.pane_width*hCarousel.pane_count);
},
next: function() {
return hCarousel.showPane(hCarousel.current_pane+1, true);
},
prev: function() {
return hCarousel.showPane(hCarousel.current_pane-1, true);
},
showPane: function( index ) {
// between the bounds
index = Math.max(0, Math.min(index, hCarousel.pane_count-1));
hCarousel.current_pane = index;
var offset = -((100/hCarousel.pane_count)*hCarousel.current_pane);
hCarousel.setContainerOffset(offset, true);
},
setContainerOffset: function( percent, animate ) {
hCarousel.container.removeClass("animate");
if(animate) {
hCarousel.container.addClass("animate");
}
if(Modernizr.csstransforms3d) {
hCarousel.container.css("transform", "translate3d("+ percent +"%,0,0) scale3d(1,1,1)");
}
else if(Modernizr.csstransforms) {
hCarousel.container.css("transform", "translate("+ percent +"%,0)");
}
else {
var px = ((hCarousel.pane_width*hCarousel.pane_count) / 100) * percent;
hCarousel.container.css("left", px+"px");
}
},
handleHammer: function( ev ) {
ev.gesture.preventDefault();
switch(ev.type) {
case 'dragright':
case 'dragleft':
// stick to the finger
var pane_offset = -(100/hCarousel.pane_count)*hCarousel.current_pane;
var drag_offset = ((100/hCarousel.pane_width)*ev.gesture.deltaX) / hCarousel.pane_count;
// slow down at the first and last pane
if((hCarousel.current_pane == 0 && ev.gesture.direction == Hammer.DIRECTION_RIGHT) ||
(hCarousel.current_pane == hCarousel.pane_count-1 && ev.gesture.direction == Hammer.DIRECTION_LEFT)) {
drag_offset *= .4;
}
hCarousel.setContainerOffset(drag_offset + pane_offset);
break;
case 'swipeleft':
hCarousel.next();
ev.gesture.stopDetect();
break;
case 'swiperight':
hCarousel.prev();
ev.gesture.stopDetect();
break;
case 'release':
// more then 50% moved, navigate
if(Math.abs(ev.gesture.deltaX) > hCarousel.pane_width/2) {
if(ev.gesture.direction == 'right') {
hCarousel.prev();
} else {
hCarousel.next();
}
}
else {
hCarousel.showPane(hCarousel.current_pane, true);
}
break;
}
}
}
And I call this like:
var hSections;
$(document).ready(function(){
hSections = hCarousel.build('.hcarousel-container');
});
Which works fine. But I want to make it so that I can have multiple carousels on the page which again works... but the overall width of the container is incorrect because it's combining the width of both carousels.
How can I run multiple instances of something like this, but the code know WHICH instance it's interacting with so things don't become mixed up, etc.
The problem is your design is not really suited to multiple instances, because of the object literal which has properties of the carousel, but also the build method.
If I was starting this from scratch, I would prefer a more OOP design, with a carousel class that can you instantiate, or have it as a jQuery plugin. That said, it's not impossible to adapt your existing code.
function hCarousel(selector){
function hCarouselInstance(element){
var hCarousel = {
// insert whole hCarousel object code
container: false,
panes: false,
build : function( element ){
...
};
this.hCarousel = hCarousel;
hCarousel.build(element);
}
var instances = [];
$(selector).each(function(){
instances.push(new hCarouselInstance(this));
});
return instances;
}
Usage
For example, all elements with the hcarousel-container class will become an independant carousel.
$(document).ready(function(){
var instances = hCarousel('.hcarousel-container');
});
Explanation:
The hCarousel function is called passing the selector, which can match multiple elements. It could also be called multiple times if needed.
The inner hCarouselInstance is to be used like a class, and instantiated using the new keyword. When hCarousel is called, it iterates over the matched elements and creates a new instance of hCarouselInstance.
Now, hCarouselInstance is a self contained function that houses your original hCarousel object, and after creating the object it calls hCarousel.build().
The instances return value is an array containing each instance object. You can access the hCarousel properties and methods from there, such as:
instances[0].hCarousel.panes;
jQuery plugin
Below is a conversion to a jQuery plugin, which will work for multiple carousels.
(function ( $ ) {
$.fn.hCarousel = function( ) {
return this.each(function( ) {
var hCarousel = {
// insert whole hCarousel object code here - same as in the question
};
hCarousel.build(this);
});
};
}( jQuery ));
Plugin usage:
$('.hcarousel-container').hCarousel();
I would try turning it into a function which you can use like a class. Then you can create separate objects for your carousels.
So you would have something like the following:
function HCarousel (element) {
this.element=element;
this.container= false;
this.panes= false;
this.pane_width= 0;
this.pane_count= 0;
this.current_pane= 0;
}
And then add each method on the class like this.
HCarousel.prototype.build = function() {
this.container = $(element).find('.hcarousel-inner-container');
this.panes = $(hCarousel.container).find('> .section');
this.pane_width = 0;
this.pane_count = hCarousel.panes.length;
this.current_pane = 0;
this.setPaneDimensions( element );
$(window).on('load resize orientationchange', function() {
this.setPaneDimensions( element );
});
$(this.element).hammer({ drag_lock_to_axis: true }).on('release dragleft dragright swipeleft swiperight', hCarousel.handleHammer);
};
etc. That should give you the basic idea. Will take a little bit of re-writing, but then you can create a carousel with something like this:
var carousel1 = new HCarousel('.hcarousel-container');
Hope that puts you on the right track.
Classes don't actually exist in JS, but this is a way to simulate one using a function. Here's a good article on using classes in JS http://www.phpied.com/3-ways-to-define-a-javascript-class/
I am using a jQuery rss widget from this page. It doesn't work in jQuery 1.9 as it shows this error:
Uncaught TypeError: Object [object Object] has no method 'live':
$('.dropDownList div').live('click',function(){
Using .live instead of .on, it manages to pull the first feed but the selection doesn't work on click.You can compare these two fiddles: (.on) and (.live). Can anyone help me solve this problem?
Old Code:
var tabs = {
"Google" : {
"feed" : "https://news.google.com/?output=rss",
"function" : rss
},
"Latest Tutorials": {
"feed" : "http://feeds.feedburner.com/Tutorialzine",
"function" : rss
},
}
var totalTabs;
$(document).ready(function(){
/* This code is executed after the DOM has been completely loaded */
/* Counting the tabs */
totalTabs=0;
$.each(tabs,function(){totalTabs++;})
$('#feedWidget').show().mouseleave(function(){
/* If the cursor left the widet, hide the drop down list: */
$('.dropDownList').remove();
$('#activeTab').removeClass('hover');
}).mouseenter(function(){
if(totalTabs>1) $('#activeTab').addClass('hover');
});
$('#activeTab').click(showDropDown);
/* Using the live method to bind an event, because the .dropDownList does not exist yet: */
$('.dropDownList div').live('click',function(){
/* Calling the showDropDown function, when the drop down is already shown, will hide it: */
showDropDown();
showTab($(this).text());
});
/* Showing one of the tabs on load: */
showTab('Google');
});
function showTab(key)
{
var obj = tabs[key];
if(!obj) return false;
var stage = $('#tabContent');
/* Forming the query: */
var query = "select * from feed where url='"+obj.feed+"' LIMIT 5";
/* Forming the URL to YQL: */
var url = "http://query.yahooapis.com/v1/public/yql?q="+encodeURIComponent(query)+"&format=json&callback=?";
$.getJSON(url,function(data){
stage.empty();
/* item exists in RSS and entry in ATOM feeds: */
$.each(data.query.results.item || data.query.results.entry,function(){
try{
/* Trying to call the user provided function, "this" the rest of the feed data: */
stage.append(obj['function'](this));
}
catch(e){
/* Notifying users if there are any problems with their handler functions: */
var f_name =obj['function'].toString().match(/function\s+(\w+)\(/i);
if(f_name) f_name = f_name[1];
stage.append('<div>There is a problem with your '+f_name+ ' function</div>');
return false;
}
})
});
$('#activeTab').text(key);
}
function showDropDown()
{
if(totalTabs<2) return false;
if($('#feedWidget .dropDownList').length)
{
/* If the drop down is already shown, hide it: */
$('.dropDownList').slideUp('fast',function(){ $(this).remove(); })
return false;
}
var activeTab = $('#activeTab');
var offsetTop = (activeTab.offset().top - $('#feedWidget').offset().top )+activeTab.outerHeight() - 5;
/* Creating the drop down div on the fly: */
var dropDown = $('<div>').addClass('dropDownList').css({
'top' : offsetTop,
'width' : activeTab.width()
}).hide().appendTo('#feedWidget')
$.each(tabs,function(j){
/* Populating the div with the tabs that are not currently shown: */
if(j==activeTab.text()) return true;
$('<div>').text(j).appendTo(dropDown);
})
dropDown.slideDown('fast');
}
function twitter(item)
{
/* Formats the tweets, by turning hashtags, mentions an URLS into proper hyperlinks: */
return $('<div>').html(
formatString(item.description || item.title)+
' [tweet]'
);
}
function rss(item)
{
return $('<div>').html(
formatString(item.title.content || item.title)+
' [read]'
);
}
function buzz(item)
{
return $('<div>').html(
formatString(item.content[0].content || item.title.content || item.title)+
' [read]'
);
}
function formatString(str)
{
/* This function was taken from our Twitter Ticker tutorial - http://tutorialzine.com/2009/10/jquery-twitter-ticker/ */
str = str.replace(/<[^>]+>/ig,'');
str=' '+str;
str = str.replace(/((ftp|https?):\/\/([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?)/gm,'$1');
str = str.replace(/([^\w])\#([\w\-]+)/gm,'$1#$2');
str = str.replace(/([^\w])\#([\w\-]+)/gm,'$1#$2');
return str;
}
.on won't work off the bat because the element isn't created yet.
Replace
$('.dropDownList').on('click','div',function(){
With
$(document).on('click','.dropDownList div',function(){
I am using this script from: http://pop.seaofclouds.com/
The problem is if you call the script multiple times it causes a cascading effect of a pop-out within a pop-out for as many times as you call the script.
I'm trying to figure out how to prevent it from executing when the popout has already been set. Here's the script:
//
// pop! for jQuery
// v0.2 requires jQuery v1.2 or later
//
// Licensed under the MIT:
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright 2007,2008 SEAOFCLOUDS [http://seaofclouds.com]
//
(function($) {
$.pop = function(options){
// inject html wrapper
function initpops (){
$(".pop").each(function() {
var pop_classes = $(this).attr("class");
if ( $(this).find('.pop_menu').length) {
// do nothing
} else {
$(this).addClass("pop_menu");
$(this).wrap("<div class='"+pop_classes+"'></div>");
$(".pop_menu").attr("class", "pop_menu");
$(this).before(" \
<div class='pop_toggle'></div> \
");
}
});
}
initpops();
// assign reverse z-indexes to each pop
var totalpops = $(".pop").length + 100;
$(".pop").each(function(i) {
var popzindex = totalpops - i;
$(this).css({ zIndex: popzindex });
});
// close pops if user clicks outside of pop
activePop = null;
function closeInactivePop() {
$(".pop").each(function (i) {
if ($(this).hasClass('active') && i!=activePop) {
$(this).removeClass('active');
}
});
return false;
}
$(".pop").mouseover(function() { activePop = $(".pop").index(this); });
$(".pop").mouseout(function() { activePop = null; });
$("body").on("click", ".pop", function(){
closeInactivePop();
});
// toggle that pop
$("body").on("click", ".pop_toggle", function(){
$(this).parent(".pop").toggleClass("active");
});
}
})(jQuery);
now when i load this script on an ajax call the new pop-out menus work but the old ones do not react to the onclick event.
You shouldn't mess with the plugin. It works exactly like it should.
Better show us how you call this on elements that you already have.
Also I don't like this plugin. Better use something from JqueryUI
You can do such thing in much easier way.
[edit]
I tried your first code (the plugin) and it works correctly for me.
[edit]
OK. I get it. You call $.pop(); multiple times. You shouldn't! Calling $.pop(); will pin up the drop down menu to all elements that has class="pop". This is the reason why you have such funny stack.
Just use $.pop(); once.
Plugin doesn't give ability to connect NEW elements that was dynamically created on the page.
Removed pop from ajax call and just called this on success:
$(".pop").each(function() {
var pop_classes = $(this).attr("class");
if ( $(this).find('.pop_menu').length) {
// do nothing
} else {
$(this).addClass("pop_menu");
$(this).wrap("<div class='"+pop_classes+"'></div>");
$(".pop_menu").attr("class", "pop_menu");
$(this).before(" \
<div class='pop_toggle'></div> \
");
}
});
// assign reverse z-indexes to each pop
var totalpops = $(".pop").length + 100;
$(".pop").each(function(i) {
var popzindex = totalpops - i;
$(this).css({ zIndex: popzindex });
});
// close pops if user clicks outside of pop
activePop = null;
function closeInactivePop() {
$(".pop").each(function (i) {
if ($(this).hasClass('active') && i!=activePop) {
$(this).removeClass('active');
}
});
return false;
}
$(".pop").mouseover(function() { activePop = $(".pop").index(this); });
$(".pop").mouseout(function() { activePop = null; });
I wrote a slideshow plugin, but for some reason maybe because I've been working on it all day, I can't figure out exactly how to get it to go back to state one, once it's reached the very last state when it's on auto mode.
I'm thinking it's an architectual issue at this point, because basically I'm attaching the amount to scroll left to (negatively) for each panel (a panel contains 4 images which is what is currently shown to the user). The first tab should get: 0, the second 680, the third, 1360, etc. This is just done by calculating the width of the 4 images plus the padding.
I have it on a setTimeout(function(){}) currently to automatically move it which works pretty well (unless you also click tabs, but that's another issue). I just want to make it so when it's at the last state (numTabs - 1), to animate and move its state back to the first one.
Code:
(function($) {
var methods = {
init: function(options) {
var settings = $.extend({
'speed': '1000',
'interval': '1000',
'auto': 'on'
}, options);
return this.each(function() {
var $wrapper = $(this);
var $sliderContainer = $wrapper.find('.js-slider-container');
$sliderContainer.hide().fadeIn();
var $tabs = $wrapper.find('.js-slider-tabs li a');
var numTabs = $tabs.size();
var innerWidth = $wrapper.find('.js-slider-container').width();
var $elements = $wrapper.find('.js-slider-container a');
var $firstElement = $elements.first();
var containerHeight = $firstElement.height();
$sliderContainer.height(containerHeight);
// Loop through each list element in `.js-slider-tabs` and add the
// distance to move for each "panel". A panel in this example is 4 images
$tabs.each(function(i) {
// Set amount to scroll for each tab
if (i === 1) {
$(this).attr('data-to-move', innerWidth + 20); // 20 is the padding between elements
} else {
$(this).attr('data-to-move', innerWidth * (i) + (i * 20));
}
});
// If they hovered on the panel, add paused to the data attribute
$('.js-slider-container').hover(function() {
$sliderContainer.attr('data-paused', true);
}, function() {
$sliderContainer.attr('data-paused', false);
});
// Start the auto slide
if (settings.auto === 'on') {
methods.auto($tabs, settings, $sliderContainer);
}
$tabs.click(function() {
var $tab = $(this);
var $panelNum = $(this).attr('data-slider-panel');
var $amountToMove = $(this).attr('data-to-move');
// Remove the active class of the `li` if it contains it
$tabs.each(function() {
var $tab = $(this);
if ($tab.parent().hasClass('active')) {
$tab.parent().removeClass('active');
}
});
// Add active state to current tab
$tab.parent().addClass('active');
// Animate to panel position
methods.animate($amountToMove, settings);
return false;
});
});
},
auto: function($tabs, settings, $sliderContainer) {
$tabs.each(function(i) {
var $amountToMove = $(this).attr('data-to-move');
setTimeout(function() {
methods.animate($amountToMove, settings, i, $sliderContainer);
}, i * settings.interval);
});
},
animate: function($amountToMove, settings, i, $sliderContainer) {
// Animate
$('.js-slider-tabs li').eq(i - 1).removeClass('active');
$('.js-slider-tabs li').eq(i).addClass('active');
$('#js-to-move').animate({
'left': -$amountToMove
}, settings.speed, 'linear', function() {});
}
};
$.fn.slider = 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 {
return false;
}
};
})(jQuery);
$(window).ready(function() {
$('.js-slider').slider({
'speed': '10000',
'interval': '10000',
'auto': 'on'
});
});
The auto and animate methods are where the magic happens. The parameters speed is how fast it's animated and interval is how often, currently set at 10 seconds.
Can anyone help me figure out how to get this to "infinitely loop", if you will?
Here is a JSFiddle
It would probably be better to let go of the .each() and setTimeout() combo and use just setInterval() instead. Using .each() naturally limits your loop to the length of your collection, so it's better to use a looping mechanism that's not, and that you can break at any point you choose.
Besides, you can readily identify the current visible element by just checking for .active, from what I can see.
You'd probably need something like this:
setInterval(function () {
// do this check here.
// it saves you a function call and having to pass in $sliderContainer
if ($sliderContainer.attr('data-paused') === 'true') { return; }
// you really need to just pass in the settings object.
// the current element you can identify (as mentioned),
// and $amountToMove is derivable from that.
methods.animate(settings);
}, i * settings.interval);
// ...
// cache your slider tabs outside of the function
// and just form a closure on that to speed up your manips
var slidertabs = $('.js-slider-tabs');
animate : function (settings) {
// identify the current tab
var current = slidertabs.find('li.active'),
// and then do some magic to determine the next element in the loop
next = current.next().length >= 0 ?
current.next() :
slidertabs.find('li:eq(0)')
;
current.removeClass('active');
next.addClass('active');
// do your stuff
};
The code is not optimized, but I hope you see where I'm getting at here.