Slide menu with javascript - javascript

I'm trying to create a new menu with job listings for my wordpress page, like the one on this page: http://www.resume.se/, where it says Senaste jobben (sorry for it being in Swedish, hope you get which menu I'm referring to). So, I want the list object to be visible for a few seconds, and then I want the menu slide to the left, the first time the width of the first list object, the second slide the width of the second list object, and so on.
if ($('.container-menu-platsannonser').length > 0 ) {
var slider = $('.menu-platsannonser').children('ul');
speed = 4000;
slider.each(function(){
var $this = $(this);
var li = $this.find('li');
var no_of = li.length;
var cur = 1;
if (3 < no_of) {
$this.slideShow = function() {
++cur;
$this.animate({
'margin-left': '-='+100
}, 600, 'swing', function(){
if (no_of == cur) {
$this.css('margin-left', '0');
cur = 1;
}
setTimeout($this.slideShow, speed);
})
}
setTimeout($this.slideShow, speed);
}
})
}
I've managed to set the slide to 100 px, but how do I get the animation width to change with each list item? Also, right now I have four menu items, created with wordpress. This code makes the list items slide two and a half time, and then it starts from the beginning. I would like the menu to loop seamlessly. Bear with me on this, since I kinda suck at javascript. Please, let me know if I'm being unclear, or if you need additional information.
So, to sum up. What I'd like to do is:
– Slide the menu to the left, the width of the list object
– Keep on looping endlessly
Appreciate any help I can get!

To get the width on the list item I think you can just use:
$("li:fist").outerWidth(true);
To keep the loop going forever I would wait for the animation to complete and move the li to the end of the list.
var $li = $("li:first");
$li.parent().append($li);

Related

Replacing Bootstrap Dropdown with Dropup (Different activity on two near identical implementations)

I'm working on a project over at github pages, which I replace a bootstrap .dropdown with .dropup if the div's overflow-y: scroll will cause the dropdown menu to be cutoff / overflow. You can see the function working properly at this jsfiddle. Notice if you click on the ellipsis icon to the right on the top rows, it will drop down, if you click on the icon on the bottom rows, it will drop up.
Now, my actual implementation (github page), the code is exactly the same (below), but it wants to replace all .dropdown classes with .dropup when opened, including the top-most row which gets cut off, seen in the photo below.
I've been struggling with this for a week and can't quite figure it out. I've tried a few different things that I thought fixed it but ended up just being a hack and didn't work on mobile, or replaced some but not all etc.
Here is the Javascript / jQuery I'm using, which can be seen in the jsfiddle and my github source here.
$(document).on("shown.bs.dropdown", ".dropdown", function () {
// calculate the required sizes, spaces
var $ul = $(this).children(".dropdown-menu");
var $button = $(this).children(".song-menu");
var ulOffset = $ul.offset();
// how much space would be left on the top if the dropdown opened that direction
var spaceUp = (ulOffset.top - $button.height() - $ul.height()) - $('#playlist').scrollTop();
// how much space is left at the bottom
var spaceDown = $('#playlist').scrollTop() + $('#playlist').height() - ((ulOffset.top + 10) + $ul.height());
// switch to dropup only if there is no space at the bottom AND there is space at the top, or there isn't either but it would be still better fit
if (spaceDown < 0 && (spaceUp >= 0 || spaceUp > spaceDown))
$(this).addClass("dropup");
}).on("hidden.bs.dropdown", ".dropdown", function() {
// always reset after close
$(this).removeClass("dropup");
});
Edit:
To clear up any confusion, here's an example of the behavior without my added .dropup function. jsfiddle Notice when you click the last menu item, it opens the menu but requires scrolling. I specifically want to remove the .dropdown class and add .dropup in this case, so no scrolling is required.
It took some basic math, but I managed to figure out what you desired to do. This code changes the bootstrap classes between dropup and dropdown depending on the room available for a normal dropdown.
I calculated this by detracting the height of the button, dropdownmenu and how far the button was scrolled down in the scrollContainer from the height of the scrollContainer. I got the value how much the div was scrolled down by using the buttons offset and detracting the offset from the scrollContainer.
Here is my jQuery (I selected the .playlist class because this was attached to your scrollContainer, but you should replace it by an id or select it by other means):
$(".dropdown, .dropup").click(function(){
var dropdownClassCheck = $(this).hasClass('dropdown');
var buttonOffset = $(this).offset().top;
var scrollboxOffset = $('.playlist').offset().top;
var buttonHeight = $(this).height();
var scrollBoxHeight = $('.playlist').height();
var dropDownButtonHeight = $(this).children('ul').height();
dropdownSpaceCheck = scrollBoxHeight>buttonOffset-scrollboxOffset+buttonHeight+dropDownButtonHeight;
if(dropdownClassCheck && !dropdownSpaceCheck){
$(this).removeClass('dropdown').addClass('dropup');
}
else if(!dropdownClassCheck && dropdownSpaceCheck){
$(this).removeClass('dropup').addClass('dropdown');
}
});
A working JSFiddle
Let me know if there are parts of the code that could be improved/done easier or if there are any problems with my solution.
I have not thoroughly checked, but .scrollTop() is probably why the code fails when combined with other elements in the DOM, so here is a solution without it:
function checkHeights(){
// LOOP through each dropdown
$('.dropdown,.dropup').each(function(index,element){
var $dropDown = $(element),
$dropDownMenu = $dropDown.find('.dropdown-menu'),
dropDownTop = $dropDown.offset().top,
visibleHeight = $dropDown.height(),
hiddenHeight = $dropDownMenu.height(),
ddTop = dropDownTop - hiddenHeight,
ddBottom = dropDownTop + visibleHeight + hiddenHeight;
// LOOP through all parents
$dropDown.parents().each(function(ix,el){
var $el = $(el);
// CHECK if any of them have overflow property set
if( $el.css('overflow') !== 'visible' ){
var limitTop = $el.offset().top,
limitBottom = limitTop + $el.height();
// CHECK if parent is better fit when dropped upside
if( limitBottom < ddBottom && ( ddTop - limitTop ) > ( limitBottom - ddBottom ) )
$dropDown.removeClass('dropdown').addClass('dropup');
else
$dropDown.removeClass('dropup').addClass('dropdown');
// BREAK LOOP
return false;
}
});
});
}
$(document).ready(function() {
checkHeights();
$('.playlist').scroll(checkHeights);
});
JS Fiddle here.
This one does not require any class or id given to it except for dropdown,dropdown-menu, and dropup (all of which are Bootstrap defaults) and would work fine even if there are multiple playlists on page.
UPDATE
The code is modified and wrapped in a function in order to allow being called when scroll event fires.
I think that the problem it's that you have a big header, and the jsFiddle don't. So ulOffset.top it's always big, and spaceDown is always negative
Replace parent div.dropdown with div.dropup.

Adding and removing classes at different heights on page using jQuery

I want to remove/add classes when the user is at different distances from the top by using jQuery.
I have successfully done it, and it works fine, but I think I'm doing it wrong, and I would like your help to optimize the code.
The html is simple, basically the sections(including the header), have 100% width. and different colors. I want to make the header change color when its over the first section(for aesthetical purposes).
And I also want it to have a shadow when the page has been scrolled more than 1 pixel.
I'm doing it by adding/removing classes.
When I use one big else if statement it doesn't work well because whenever any any condition is matched js stops checking for other matches, so it doesn't apply all the classes needed.
The next code works, however as I said, I think that it's not optimal/bad written.
Here is the HTML markup:
<header class="dark no-shadow">
Header
</header>
<section class="blue">
Please Scroll Down to see the header changes...
</section>
<section>
The header color Should change when you pass through me.
</section>
And here is the jQuery code:
var header = $('header'),
blueSection = $('section.blue'),
// Calculate when to change the color.
offset = blueSection.offset().top + blueSection.height() - header.height();
$(window).scroll(function(){
var scroll = $(window).scrollTop();
// Remove Class "dark" after scrolling over the dark section
if (scroll >= offset) {
header.removeClass('dark');
} else {
header.addClass('dark');
}
// Remove Class "no-shadows" whenever not on the top of the page.
if (scroll >= 1) {
header.removeClass('no-shadow');
} else {
header.addClass('no-shadow');
}
});
And for those of you who like to use jsfiddle(like me!):
https://jsfiddle.net/shock/wztdt077/6/
Thanks ahead guys!
Here is what I've come up with:
var header = $('header'),
blueSection = $('section.blue'),
// Calculate when to change the color.
offset = blueSection.offset().top + blueSection.height() - header.height();
var add = function(obj, cls) {obj.addClass(cls);}
var remove = function(obj, cls) {obj.removeClass(cls);}
var stylePoints = [offset, 1, 100, 200];
var styleTo = ['dark', 'no-shadow', 'blue', 'tall'];
var styleType = [add, add, remove, remove];
$(window).scroll(function() {
var scroll = $(window).scrollTop();
for (i = 0; i < stylePoints.length; i++) {
var func = styleType[i];
if (scroll >= stylePoints[i])
(styleType[i] == add) ? remove(header, styleTo[i]) : add(header, styleTo[i]);
else func(header, styleTo[i]);
}
});
It's not that much longer than your current jQuery, and allows for (theoretically) an infinite number of style changes without having to add a million long if/else statements. To add a new style change, you have to add a value to the end of each of the three arrays. stylePoints specifies the scrollTop() value at which a style should either be added or removed. styleTo specifies the class to be added or removed. styleType specifies whether this class should be added or removed when the user is scrolled above the corresponding stylePoints value. The opposite will occur when the user is scrolled below or at the corresponding stylePoints value. For instance, you can see from the code that the tall class will be removed from the header when the user is scrolled above 200, and added when the user is scrolled below or at 200.

How can I set my browser window's scrollbar or a div scrollbar to scroll in increments using animate and scrollTop?

The general idea to the site i am designing is to scroll through a set of menu items horizontally and incrementally underneath a static div that will magnify(increase dimensions and pt size) the contents of a menu items. I don't really need help with the magnify portion because i think it's as simple as adding a mag class to any of the menuItem divs that go underneath the static div. I have been messing with this for a few weeks and the code I have for incrementally scrolling, so far, is this:
$(document).ready(function () {
currentScrollPos = $('#scrollableDiv').scrollTop(120); //sets default scroll pos
/*The incrementScroll function is passed arguments currentScrollPos and UserScroll which are variables that i have initiated earlier in the program, and then initiates a for loop.
-The first statement sets up the variables: nextScrollPos as equal to the currentScrollPos(which by default is 120px) plus 240px(the distance to next menuItem), prevScrollPos as equal to the currentScrollPos(which by default is 120px) minus 240px(the distance to next menuItem).
-The second Statement checks to see if the user has scrolled using var userScroll
-The third statement sets: var CurrentScroll equal to the new scroll position and var userScroll to false*/
function incrementScroll(currentScrollPos, userScroll) {
for (var nextScrollPos = parseInt(currentScrollPos + 240, 10),
prevScrollPos = parseInt(currentScrollPos - 240, 10); //end first statement
userScroll == 'true'; console.log('dude'), //end second statement and begining of third
currentScrollPos = scrollTop(), userScroll = 'false') {
if (scrollTop() < currentScrollPos) {
$('#scrollableDiv').animate({
scrollTop: (parseInt(prevScrollPos, 10))
}, 200);
console.log('scrolln up')
} else if (scrollTop() > currentScrollPos) {
$('#scrollableDiv').animate({
scrollTop: (parseInt(nextScrollPos, 10))
}, 200);
console.log('scrolln down')//fire when
}
}
}
$('#scrollableDiv').scroll(function () {
userScroll = 'true';
_.debounce(incrementScroll, 200); //controls the amount of times the incrementScroll function is called
console.log('straight scrolln')
});
});
I have found a variety of solutions that are nigh close: such as a plugin that snaps to the next or previous div horizontally demo, another solution that also snaps and is based on setTimeout demo, but nothing that nails incrementally scrolling through divs. I also found a way to control the rate at which a user may scroll through the menuItems using debounce which is included in the above code.
The console.logs inside the loop do not fire when I demo the code in jsfiddle which leads me to believe the problem lies within the loop. I'm a noob though so it could be in syntax or anywhere else in the code for that matter. Also in the second demo, i have provided the css for the horizontal static div, but the moment I put it in my html it keeps the js from working.
I would like to write the code instead of using a plugin and any help would be appreciated! Also, thank you ahead of time!
Try this fiddle. Menu container height is 960px to show 4 menu items. "Zoom" div is positioned absolutely at top. When you scroll mouse over this div, menu items shifts to top/bottom. I had to add additional div to bottom to be able to scroll to last 3 menu items. JS code:
jQuery(document).ready(function($){
var current = 0;
var menu = $('.menu-container').scrollTop(0);
var items = menu.find('.menu-item');
var zoom = $('.zoom');
function isVerticalScroll(event){
var e = event.originalEvent;
if (e.axis && e.axis === e.HORIZONTAL_AXIS)
return false;
if (e.wheelDeltaX)
return false;
return true;
}
function handleMouseScroll(event){
if(isVerticalScroll(event)){
var delta = event.originalEvent.wheelDelta * -1 || event.originalEvent.detail;
current += (delta > 0 ? 1 : -1);
if(current < 0)
current = 0;
if(current >= items.length){
current = items.length - 1;
}
menu.stop().animate({
"scrollTop": current * 240
}, 300);
items.removeClass('current').eq(current).addClass('current');
event && event.preventDefault();
return false;
}
}
zoom.on({
"MozMousePixelScroll": handleMouseScroll,
"mousewheel": handleMouseScroll
});
});
Hope it will help.

Sliding Carousel - Pagination not directing to correct location

My carousel has been built so that it slides to the next frame automatically every 5 seconds, for which I have written:
var carousel = $('.carousel ul:first');
var pagination = $('.carousel ul:last');
function slide01() {
carousel.delay(5000).animate({'margin-left':'-100%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(2)').addClass('on');
});
slide02();
};
function slide02() {
carousel.delay(5000).animate({'margin-left':'-200%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(3)').addClass('on');
});
slide03();
};
function slide03() {
carousel.delay(5000).animate({'margin-left':'-300%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(4)').addClass('on');
});
slide04();
};
function slide04() {
carousel.delay(5000).animate({'margin-left':'0%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(1)').addClass('on');
});
slide01();
};
slide01();
While this is working, I'm having difficulties with the pagination buttons. They respond but instead of going to its appropriate page, it's going to the next page, which you can view in my FIDDLE.
This is the code I have written for each button:
pagination.find('li:nth-child(1)').click(function(){
carousel.stop().animate({'margin-left':'0%'});
});
pagination.find('li:nth-child(2)').click(function(){
carousel.stop().animate({'margin-left':'100%'});
});
pagination.find('li:nth-child(3)').click(function(){
carousel.stop().animate({'margin-left':'200%'});
});
pagination.find('li:nth-child(4)').click(function(){
carousel.stop().animate({'margin-left':'300%'});
});
What can I do to fix this?
I have updated your Javascript code to provide your desired functionality and made it more extendable. Please let me know if you have any questions.
var carousel = $('.carousel ul:first');
var pagination = $('.carousel ul:last');
var slideWidth = 200;
var autoSlideTiming = 5000;
var timeout = null;
// Buttons
$.each(pagination.children(), function(i){
$(this).click(function(){
pagination.find('li.on').removeClass('on');
//if you wanted to find the width dynamically
//var slideWidth = $(this).parent().parent().find('ul:eq(0) li:eq('+i+')').width();
carousel.stop().animate({'margin-left': -(i * slideWidth)});
$(this).addClass('on');
});
});
// Slider
function advanceSlide(){
var currIndex = $(".pagination li.on").index();
var nextIndex = ((pagination.children().length - 1) == currIndex) ? 0 : (currIndex + 1);
carousel.animate({'margin-left': -(nextIndex * slideWidth)}, function() {
pagination.find('li.on').removeClass('on');
pagination.find(':nth-child('+(nextIndex + 1)+')').addClass('on');
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
});
}
$('.carousel').hover(function(){
clearTimeout(timeout);
}, function(){
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
});
//init auto slide
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
Also here is an updated Fiddle
Explanation
The $.each is a jQuery utility function that works like a normal for loop (I would actually recommend a vanilla JavaScript for loop if you know how). This $.each accepts 2 arguments: a collection (pagination.children() in our case, which is an array of the li's), and a callback function. This callback function passes in i which is our zero-based index of the collection. So, it's not 1, 2, 3, 4 but 0, 1, 2, 3. In this loop this is each li, which it is attaching a click event that handles removing and applying your on class and also the animation. The -(i * slideWidth) is taking the current zero-based index and multiplying it times the slideWidth and then getting the negative value of that result. So, the first slide -(0 * 200) would animate the margin-left to -0 or 0, and the second slide -(1 * 200) would animate the margin-left to -200, which pulls it 200 pixels in the left direction making it slide. This will allow you to had new li's to your pagination and this code will still work!
The trick to making the advanceSlide() function dynamic, is finding what the index of the next slide is. To find out where we need to go we first must find where we are. That is what currIndex is for, which we find with the jQuery index() function, which returns the zero-based index of the li with the on class. Now the magic. The nextIndex variable is being set using a ternary operator to find the value. A ternary is just programmer shorthand for a basic if then else statement. So if the ternary in this code was written out the normal way it would look like this:
var nextIndex;
if( (pagination.children().length - 1) == currIndex ){
nextIndex = 0;
}else{
nextIndex = currIndex + 1;
}
Remeber our currIndex is zero-based, but our collection (pagination.children()) length (number of items in the total collection) is not zero-based so we must minus 1 from it. Then we are checking if that value is equal to our currIndex because if this is the last item in our collection we need to set our nextIndex back to 0 so the slideshow loops back around. If it's not the last index we just add 1 to advance to the next slide. After we find that nextIndex we do our animation and apply our class.
I know this is a little long winded, but it's important that you fully understand the code you implement! Please let me know if you have nay further questions!
You have a "Maximum call stack size exceeded." error. Yours slide0X function calls another slide0Y function and so on. You should invoke slide0Y only once the slide0X animation ended (inside the complete callback).
Moreover in your click callbacks, margin-left should be negative since you want to slide to the left.
BTW, I would implement a function slideTo(slideId) so that it will work for n slides, instead of writing a function for each slide.

Jquery Tools & Scrollable

I have scrollable with the navigator plugin and history set to true. This is so that users can use id'd links to scroll to the selection of their choice in the scrollable list (about 50 items). I have circular set to true.
The items show in groups of 4. But the previous and next buttons only move by 1 item, whereas I want them to move by 4 items. How do I do this?
I can't group them in div pages since the indexed tab list then matches to these div pages rather than the contained items.
I tried creating a click handler with api.move(-2), but all kinds of strange things happen (doesn't pre-empt native scroll-by-one; rewinds instead of advancing, or advances and then rewinds in one sequence - not acceptable).
I'm obviously missing something basic. Pointers would be appreciated.
Thanks,
Henrik
Here's what I ended up doing: I grabbed the api and replaced the next and prev functions; disabled circular; added first and last buttons. Sort of a workaround/semi-solution. You can see the result at http://cityrinks.ca/wiki/wiki.php?n=ListOfRinks.TableOfRinks
$(function() {
$(".scrollable")
.scrollable({
vertical: true,
mousewheel: true,
circular:false
})
.navigator({
navi: "#rinktabs",
naviItem: 'a',
activeClass: 'current',
history: false
});
var api = $(".scrollable").data("scrollable");
api.next = function(time) {
index = this.getIndex();
size = this.getSize();
interval = size - index;
if (interval > 4) interval = 4;
return this.move(interval, time);
};
api.prev = function(time) {
index = this.getIndex();
if (index > 4) index = 4;
return this.move(-index, time);
};
$("#firstbutton").click(function() {
var api = $(".scrollable").data("scrollable");
return api.begin(api.speed);
});
$("#lastbutton").click(function() {
var api = $(".scrollable").data("scrollable");
return api.end(api.speed);
});
});

Categories