I have built a slider using SCSS, JavaScript, and HTML. The demo is here: https://jsfiddle.net/rr7g6a1b/
let mySlider = {
initializeSlider: function (options) {
let slider = options.container;
let slides = slider.querySelectorAll('.slide');
let initialSlide = slider.querySelector('.slide[data-index="0"]');
let initialButton = slider.querySelector('.pagination-button[data-index="0"]');
initialSlide.classList.add('active');
initialButton.classList.add('active');
let sliderControlButton = slider.querySelector('.control-slider-button');
if (options.autoplay === true) {
sliderControlButton.classList.add('stop');
} else {
sliderControlButton.classList.add('play');
}
sliderControlButton.addEventListener('click', function () {
mySlider.switchAutoplay(options);
});
for (let i = 0; i < slides.length; i++) {
let button = slider.querySelector('.pagination-button[data-index="' + i + '"]');
button.addEventListener('click', function () {
mySlider.goToSlide(options, i);
mySlider.stopAutoplay(options);
});
}
if (options.autoplay === true) {
options.autoplayer = window.setTimeout(function () {
mySlider.goToNext(options);
}, options.duration);
}
},
switchAutoplay: function (options) {
let sliderControlButton = options.container.querySelector('.control-slider-button');
if (options.autoplay === true) {
mySlider.stopAutoplay(options);
sliderControlButton.classList.add('play');
sliderControlButton.classList.remove('stop');
} else {
mySlider.startAutoplay(options);
sliderControlButton.classList.remove('play');
sliderControlButton.classList.add('stop');
}
},
goToSlide: function (options, target) {
let slider = options.container;
let targetSlide = slider.querySelector('.slide[data-index="' + target + '"]');
if (!targetSlide.classList.contains('active')) {
let activeSlide = slider.querySelector('.slide.active');
let activeButton = slider.querySelector('.pagination-button.active');
let activeNum = activeSlide.dataset.index;
let targetNum = parseInt(target);
let go = false;
let direction = 'left';
if (targetNum < activeNum) {
direction = 'right';
}
let targetButton = slider.querySelector('.pagination-button[data-index="' + target + '"]');
activeButton.classList.remove('active');
targetButton.classList.add('active');
let moveTarget = function (direction) {
let correct = direction;
let opposite = (direction === 'left') ? 'right' : 'left';
correct = 'hide-' + direction;
opposite = 'hide-' + opposite;
targetSlide.classList.add('transitioning');
window.setTimeout(function () {
targetSlide.classList.remove(correct);
targetSlide.classList.add(opposite);
}, 100);
window.setTimeout(function () {
targetSlide.classList.remove('transitioning');
}, 150);
};
let moveActive = function (direction) {
activeSlide.classList.remove('active');
direction = 'hide-' + direction;
activeSlide.classList.add(direction);
};
moveTarget(direction);
window.setTimeout(function () {
moveActive(direction);
targetSlide.classList.remove('hide-left');
targetSlide.classList.remove('hide-right');
targetSlide.classList.add('active');
}, 200);
}
},
goToNext: function (options) {
if (options.autoplay === true) {
let slider = options.container;
let currentSlide = slider.querySelector('.slide.active');
let currentSlideIndex = currentSlide.dataset.index;
let lastSlide = slider.querySelectorAll('.slide').length - 1;
let nextSlide = 0;
if (currentSlideIndex != lastSlide) {
nextSlide = parseInt(currentSlideIndex) + 1;
}
mySlider.goToSlide(options, nextSlide);
options.autoplayer = window.setTimeout(function () {
mySlider.goToNext(options);
}, options.duration);
}
},
stopAutoplay: function (options) {
options.autoplay = false;
window.clearTimeout(options.autoplayer);
},
startAutoplay: function (options) {
options.autoplay = true;
options.autoplayer = window.setTimeout(function () {
mySlider.goToNext(options);
}, options.duration);
}
};
let mainSlider = document.getElementById('main-slider');
let mainSliderOptions = {
container: mainSlider,
autoplay: false,
duration: 6000
}
mySlider.initializeSlider(mainSliderOptions);
The pagination buttons work fine in Chrome but in Safari, they occasionally flicker or get cut off after you click on the buttons. Is there an error/workaround for this kind of flickering? I'm not sure what could be causing this issue.
Css workaround for -webkit- browsers. Try adding the following to the flickering button's css.
-webkit-perspective: 1000;
-webkit-backface-visibility: hidden;
-webkit-transform: translate3d(0,0,0);
Related
I have a javascript function for a slider in the JS file of my site. When we are in a page where the slider is not called or there is no trigger element, it displays an error in the console "Uncaught TypeError: Cannot read properties of null (reading 'querySelector') ", This error is not displayed when the slider is in the page.
I would like to know how to avoid this error, and how to prevent this function from loading in pages where this slider is not present?
function slider(set) {
const sliderContainer = document.querySelector(set.name),
slider = sliderContainer.querySelector('.slider'),
sliderItem = slider.querySelectorAll('.slider__item'),
sliderArrows = sliderContainer.querySelectorAll('.arrows__item');
let dotsCreate,
dotsClass,
dotsFunk,
numberSlider,
numberSliderWork,
sliderExecutionLine,
sliderExecutionLineWork;
// calculate the maximum width of all slides
function forSliderItem(t) {
t = 0;
for(let i = 0; i < sliderItem.length - 1; i++) {
t += sliderItem[i].clientWidth;
}
return t;
}
let maxWidth = forSliderItem(sliderItem), // maximum width of all slides
slidWidth = 0, // main variable for calculating the movement of the slider
count = 0; // counter
//===== flip left
sliderArrows[0].addEventListener('click', function() {
if(count !== 0) {
count--;
slidWidth -= sliderItem[count].clientWidth;
slider.style.transform = `translateX(-${slidWidth}px)`;
} else {
count = sliderItem.length - 1;
slidWidth = maxWidth;
slider.style.transform = `translateX(-${slidWidth}px)`;
}
if(set.dots) {
dotsFunk();
}
if(set.numberSlid) {
numberSliderWork(count);
}
if(set.line) {
sliderExecutionLineWork(count);
}
});
//===== flip right
sliderArrows[1].addEventListener('click', function() {
if(count < sliderItem.length - 1) {
count++;
slidWidth += sliderItem[count].clientWidth;
slider.style.transform = `translateX(-${slidWidth}px)`;
} else {
count = 0;
slidWidth = 0;
slider.style.transform = `translateX(-${slidWidth}px)`;
}
if(set.dots) {
dotsFunk();
}
if(set.numberSlid) {
numberSliderWork(count);
}
if(set.line) {
sliderExecutionLineWork(count);
}
});
//===== dots
if(set.dots) {
dotsCreate = function() {
const dotContainer = document.createElement('div'); // create dots container
dotContainer.classList.add('dots');
// create the required number of dots and insert a container into the dots
sliderItem.forEach(() => {
let dotsItem = document.createElement('span');
dotContainer.append(dotsItem);
});
sliderContainer.append(dotContainer);
};
dotsCreate();
// add the class to the desired dots, and remove from the rest
dotsClass = function(remove, add) {
remove.classList.remove('dots_active');
add.classList.add('dots_active');
};
// move slides by clicking on the dot
dotsFunk = function() {
const dotsWork = sliderContainer.querySelectorAll('.dots span'); // we get dots
dotsWork.forEach((item, i) => {
dotsClass(dotsWork[i], dotsWork[count]);
item.addEventListener('click', function() {
count = i;
// multiply the slide size by the number of the dots, and get the number by which you need to move the slider
slidWidth = sliderItem[0].clientWidth * i;
slider.style.transform = `translateX(-${slidWidth}px)`;
for(let j = 0; j < dotsWork.length; j++) {
dotsClass(dotsWork[j], dotsWork[count]);
}
if(set.dots && set.numberSlid) {
numberSliderWork(count);
}
if(set.line) {
sliderExecutionLineWork(count);
}
});
});
};
dotsFunk();
}
//===== count slider
if(set.numberSlid) {
numberSlider = function(item) {
const countContainer = document.createElement('div'),
sliderNumber = document.createElement('span'),
slash = document.createElement('span'),
allSliderNumber = document.createElement('span');
casClient = document.createElement('p');
sliderNumber.innerHTML = item + 1;
casClient.innerHTML = 'Cas client N°';
slash.innerHTML = '/';
allSliderNumber.innerHTML = sliderItem.length;
countContainer.classList.add('count-slides');
countContainer.append(casClient, sliderNumber, slash, allSliderNumber);
sliderContainer.append(countContainer);
};
numberSlider(0);
numberSliderWork = function(item) {
const sliderNumberNow = sliderContainer.querySelector('.count-slides span');
sliderNumberNow.innerHTML = item + 1;
if(set.line) {
sliderExecutionLineWork(item);
}
};
}
}
slider({
name: ".video_users",
numberSlid: true
});
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Example of a page without the slider</title>
</head>
<body class="error_404">
<h1>Example of a page without the slider</h1>
<script src="media/js/faveod.js" async></script>
</body>
</html>
Just return if the element isn't present if you don't want it to show an error.
const sliderContainer = document.querySelector(set.name);
if (!sliderContainer) return;
const slider = sliderContainer.querySelector('.slider'),
sliderItem = slider.querySelectorAll('.slider__item'),
sliderArrows = sliderContainer.querySelectorAll('.arrows__item');
If you want it to only execute on certain pages, then just add:
if (location.pathname !== '/the/page/with/slider') return;
to the beginning of the function
/*
* SCROLLBAR 2 COLUMN / SOLUTIONS
*/
var ssb = {
aConts : [],
mouseY : 0,
N : 0,
asd : 0, /*active scrollbar element*/
sc : 0,
sp : 0,
to : 0,
// constructor
scrollbar : function (cont_id) {
var cont = document.getElementById(cont_id);
// perform initialization
if (! ssb.init()) return false;
var cont_clone = cont.cloneNode(false);
cont_clone.style.overflow = "hidden";
cont.parentNode.appendChild(cont_clone);
cont_clone.appendChild(cont);
cont.style.position = 'absolute';
cont.style.left = cont.style.top = '0px';
cont.style.width = cont.style.height = '100%';
// adding new container into array
ssb.aConts[ssb.N++] = cont;
cont.sg = false;
//creating scrollbar child elements
cont.st = this.create_div('ssb_st', cont, cont_clone);
cont.sb = this.create_div('ssb_sb', cont, cont_clone);
cont.su = this.create_div('ssb_up', cont, cont_clone);
cont.sd = this.create_div('ssb_down', cont, cont_clone);
// on mouse down processing
cont.sb.onmousedown = function (e) {
if (! this.cont.sg) {
if (! e) e = window.event;
ssb.asd = this.cont;
this.cont.yZ = e.screenY;
this.cont.sZ = cont.scrollTop;
this.cont.sg = true;
// new class name
this.className = 'ssb_sb ssb_sb_down';
}
return false;
}
// on mouse down on free track area - move our scroll element too
cont.st.onmousedown = function (e) {
if (! e) e = window.event;
ssb.asd = this.cont;
ssb.mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
for (var o = this.cont, y = 0; o != null; o = o.offsetParent) y += o.offsetTop;
this.cont.scrollTop = (ssb.mouseY - y - (this.cont.ratio * this.cont.offsetHeight / 2) - this.cont.sw) / this.cont.ratio;
this.cont.sb.onmousedown(e);
}
// onmousedown events
cont.su.onmousedown = cont.su.ondblclick = function (e) { ssb.mousedown(this, -1); return false; }
cont.sd.onmousedown = cont.sd.ondblclick = function (e) { ssb.mousedown(this, 1); return false; }
//onmouseout events
cont.su.onmouseout = cont.su.onmouseup = ssb.clear;
cont.sd.onmouseout = cont.sd.onmouseup = ssb.clear;
// on mouse over - apply custom class name: ssb_sb_over
cont.sb.onmouseover = function (e) {
if (! this.cont.sg) this.className = 'ssb_sb ssb_sb_over';
return false;
}
// on mouse out - revert back our usual class name 'ssb_sb'
cont.sb.onmouseout = function (e) {
if (! this.cont.sg) this.className = 'ssb_sb';
return false;
}
// onscroll - change positions of scroll element
cont.ssb_onscroll = function () {
this.ratio = (this.offsetHeight - 2 * this.sw) / this.scrollHeight;
this.sb.style.top = Math.floor(this.sw + this.scrollTop * this.ratio) + 'px';
}
// scrollbar width
cont.sw = 16;
// start scrolling
cont.ssb_onscroll();
ssb.refresh();
// binding own onscroll event
cont.onscroll = cont.ssb_onscroll;
return cont;
},
// initialization
init : function () {
if (window.oper || (! window.addEventListener && ! window.attachEvent)) { return false; }
// temp inner function for event registration
function addEvent (o, e, f) {
if (window.addEventListener) { o.addEventListener(e, f, false); ssb.w3c = true; return true; }
if (window.attachEvent) return o.attachEvent('on' + e, f);
return false;
}
// binding events
addEvent(window.document, 'mousemove', ssb.onmousemove);
addEvent(window.document, 'mouseup', ssb.onmouseup);
addEvent(window, 'resize', ssb.refresh);
return true;
},
// create and append div finc
create_div : function(c, cont, cont_clone) {
var o = document.createElement('div');
o.cont = cont;
o.className = c;
cont_clone.appendChild(o);
return o;
},
// do clear of controls
clear : function () {
clearTimeout(ssb.to);
ssb.sc = 0;
return false;
},
// refresh scrollbar
refresh : function () {
for (var i = 0, N = ssb.N; i < N; i++) {
var o = ssb.aConts[i];
o.ssb_onscroll();
o.sb.style.width = o.st.style.width = o.su.style.width = o.su.style.height = o.sd.style.width = o.sd.style.height = o.sw + 'px';
o.sb.style.height = Math.ceil(Math.max(o.sw * .5, o.ratio * o.offsetHeight) + 1) + 'px';
}
},
// arrow scrolling
arrow_scroll : function () {
if (ssb.sc != 0) {
ssb.asd.scrollTop += 6 * ssb.sc / ssb.asd.ratio;
ssb.to = setTimeout(ssb.arrow_scroll, ssb.sp);
ssb.sp = 32;
}
},
/* event binded functions : */
// scroll on mouse down
mousedown : function (o, s) {
if (ssb.sc == 0) {
// new class name
o.cont.sb.className = 'ssb_sb ssb_sb_down';
ssb.asd = o.cont;
ssb.sc = s;
ssb.sp = 100;
ssb.arrow_scroll();
}
},
// on mouseMove binded event
onmousemove : function(e) {
if (! e) e = window.event;
// get vertical mouse position
ssb.mouseY = e.screenY;
if (ssb.asd.sg) ssb.asd.scrollTop = ssb.asd.sZ + (ssb.mouseY - ssb.asd.yZ) / ssb.asd.ratio;
},
// on mouseUp binded event
onmouseup : function (e) {
if (! e) e = window.event;
var tg = (e.target) ? e.target : e.srcElement;
if (ssb.asd && document.releaseCapture) ssb.asd.releaseCapture();
// new class name
if (ssb.asd) ssb.asd.sb.className = (tg.className.indexOf('scrollbar') > 0) ? 'ssb_sb ssb_sb_over' : 'ssb_sb';
document.onselectstart = '';
ssb.clear();
ssb.asd.sg = false;
}
}
window.onload = function() {
ssb.scrollbar('container'); // scrollbar initialization
}
I am trying to make a function which will close my tooltip by 'click' on the parent element which returns me this tooltip by first click on it. Need to get the closing function exclusively for tooltip parent element. Right now everything is working, but i can also close my tooltip by clicking anywhere on body element.
`(function () {
function Tooltip(options) {
if (!options) options = {};
var self = this;
this.tooltips;
this.offset = 5;
this.beforeTooltip = options.beforeTooltip;
this.afterTooltip = options.afterTooltip;
this.tooltipWrapper = document.createElement('div');
this.status = false;
this.tooltip = function (elem) {
if (!elem.classList.contains('active')){
if (this.status) this.remElemActive();
if (this.beforeTooltip) this.beforeTooltip(elem);
elem.classList.add('active');
var coords = this.getCoords(elem);
this.tooltipWrapper.textContent = elem.dataset.tooltip;
this.tooltipWrapper.classList.add('active');
this.tooltipWrapper.style.top = coords.top - (this.tooltipWrapper.offsetHeight + this.offset) + 'px';
this.tooltipWrapper.style.left = (coords.left + coords.width / 2) - (this.tooltipWrapper.offsetWidth / 2) + 'px';
this.status = true;
if (this.status){
setTimeout(function () {
document.addEventListener('click', self.closeTipsBody, false);
}, 100)
}
if (this.afterTooltip) this.afterTooltip(elem)
}else {
elem.classList.remove('active');
}
};
this.closeTipsBody = function (e) {
if (self.tooltipWrapper === e.target || e.target.classList.contains('active')){
return false
}
self.closeTips();
};
this.closeTips = function () {
this.tooltipWrapper.classList.remove('active');
this.remElemActive();
this.status = false;
document.removeEventListener('click', self.closeTipsBody, false)
};
this.remElemActive = function () {
document.querySelector('.tooltip-js').classList.remove('active')
};
this.getCoords = function (elem) {
elem = elem.getBoundingClientRect();
return{
top: elem.top + window.pageYOffset,
left: elem.left + window.pageXOffset,
width: elem.width
}
};
this.init = function () {
document.addEventListener('DOMContentLoaded', function () {
this.tooltips = document.querySelectorAll('.tooltip-js');
this.tooltipWrapper.classList.add('tooltip-box');
document.querySelector('body').appendChild(this.tooltipWrapper);
for (var i = 0; i < this.tooltips.length; i++ ){
this.tooltips[i].addEventListener('click', function (e) {
e.preventDefault();
self.tooltip(this);
})
}
}.bind(this))
};
this.init();
}
window.Tooltip = Tooltip;
})();`
if you need any additional info, about what do i want to get to, text me.
I am testing my page in Chrome. I right click on the element that I am having issues with and I get this:
element.style {
height: 147px;
position: relative;
width: 100%;
overflow: hidden;
}
without a file attached to it. This tells me that the element style is coming from either an inline code (my page has none) or a js file (which my page has a number).
When I turn off the position: relative, I no longer have any issues.
I think I have narrowed it down to one js file that is screwing up my page, but I don't have a clue as to how to fix it or how to figure it out.
Here are some images better describing the issue:
http://i.imgur.com/Yxv2zLe.png
http://i.imgur.com/wtqkqw5.png
If you look at the first image, the blue box over the image needs to be directly over the orange box so that everything lines up properly.
In the second image, when I turn off the position, the image is positions correctly under the overlay.
My problem is that I don't know enough about js to fix the code to fix the position.
Here is the code that I think is responsible, if it helps.
/* ---------------------------------------------------------------------- */
/* Entry Slider
/* ---------------------------------------------------------------------- */
if ($().cycle) {
var entrySliders = $('.entry-slider > ul');
$.fn.cycle.transitions.scrollHorizontal = function ($cont, $slides, opts) {
$cont.css('overflow', 'hidden');
opts.before.push($.fn.cycle.commonReset);
var w = $cont.width();
opts.cssFirst.left = 0;
opts.cssBefore.left = w;
opts.cssBefore.top = 0;
opts.animIn.left = 0;
opts.animOut.left = 0 - w;
if ($cont.data('dir') === 'prev') {
opts.cssBefore.left = -w;
opts.animOut.left = w;
}
};
function initEntrySlider(entrySliders, isFirstTime) {
entrySliders.each(function (i) {
var slider = $(this);
var initPerformed = isFirstTime && slider.data('initInvoked');
if (!initPerformed) {
slider.data('initInvoked', 'true');
var sliderId = 'entry-slider-' + i;
slider.attr('id', sliderId);
var prevButtonId = sliderId + '-prev';
var nextButtonId = sliderId + '-next';
if (slider.data('enable') === 'false') {
return;
}
slider.css('height', slider.children('li:first').height());
var firstSlide = slider.children('li')[0];
var lastSlide = slider.children('li')[slider.children('li').length - 1];
if (slider.children('li').length > 1) {
if (slider.parent().find('#' + prevButtonId).length == 0) {
slider.parent().append('<div class="entry-slider-nav"><a id="' + prevButtonId + '" class="prev">Prev</a><a id="' + nextButtonId + '" class="next">Next</a></div>');
}
}
slider.cycle({
onPrevNextEvent: function (isNext, zeroBasedSlideIndex, slideElement) {
$(slideElement).parent().data('dir', isNext ? 'next' : 'prev');
},
before: function (curr, next, opts, forwardFlag) {
var $this = $(this);
var sliderId = $this.closest('ul').attr('id');
// set the container's height to that of the current slide
$this.parent().stop().animate({height: $this.height()}, opts.speed);
if (opts['nowrap']) {
var prevButton = $('#' + sliderId + '-prev');
var nextButton = $('#' + sliderId + '-next');
if ((firstSlide == next) && (!prevButton.hasClass('disabled'))) {
prevButton.addClass('disabled');
} else {
prevButton.removeClass('disabled');
}
if ((lastSlide == next) && (!nextButton.hasClass('disabled'))) {
nextButton.addClass('disabled');
} else {
nextButton.removeClass('disabled');
}
}
},
containerResize: false,
pauseOnPagerHover: true,
nowrap: false, // if true, the carousel will not be circular
easing: 'easeInOutExpo',
fx: 'scrollHorizontal',
speed: 600,
timeout: 0,
fit: true,
width: '100%',
pause: true,
slideResize: true,
slideExpr: 'li',
prev: '#' + prevButtonId,
next: '#' + nextButtonId
});
}
});
if (Modernizr.touch && $().swipe) {
function doEntrySliderSwipe(e, dir) {
var sliderId = $(e.currentTarget).attr('id');
if (dir == 'left') {
$('#' + sliderId + '-next').trigger('click');
}
if (dir == 'right') {
$('#' + sliderId + '-prev').trigger('click');
}
}
entrySliders.each(function () {
var slider = $(this);
var initPerformed = isFirstTime && slider.data('swipeInvoked');
if (!initPerformed) {
slider.data('swipeInvoked', 'true');
slider.swipe({
click: function (e, target) {
$(target).trigger('click');
},
swipeLeft: doEntrySliderSwipe,
swipeRight: doEntrySliderSwipe,
allowPageScroll: 'auto'
});
}
});
}
}
function initAllEntrySliders(isFirstTime) {
if (isFirstTime) {
var timer = window.setTimeout(function () {
window.clearTimeout(timer);
initEntrySlider($('.entry-slider > ul'), isFirstTime);
}, 100);
} else {
initEntrySlider($('.entry-slider > ul'), isFirstTime);
}
}
function resizeEntrySlider(entrySliders) {
entrySliders.each(function () {
var slider = $(this);
slider.css('height', slider.children('li:first').height());
});
}
function loadEntrySlider() {
var entrySliderImages = $('.entry-slider > ul > li> a > img');
var unloadedImagesCount = 0;
var unloadedImages = [];
entrySliderImages.each(function () {
if (!this.complete && this.complete != undefined) {
unloadedImages.push(this);
unloadedImagesCount++;
}
});
if (unloadedImagesCount == 0) {
initAllEntrySliders(true);
} else {
var initAllEntrySlidersInvoked = false;
var loadedImagesCount = 0;
$(unloadedImages).bind('load', function () {
loadedImagesCount++;
if (loadedImagesCount === unloadedImagesCount) {
if (!initAllEntrySlidersInvoked) {
initAllEntrySlidersInvoked = true;
initAllEntrySliders(true);
}
}
});
var timer = window.setTimeout(function () {
window.clearTimeout(timer);
$(unloadedImages).each(function () {
if (this.complete || this.complete === undefined) {
$(this).trigger('load');
}
});
}, 50);
}
}
loadEntrySlider();
$(window).on('resize', function () {
var timer = window.setTimeout(function () {
window.clearTimeout(timer);
resizeEntrySlider(entrySliders);
}, 30);
});
}
And if I should be asking this somewhere else or something, let me know.
I have a list of div's all with a set and equal height/width that are float:left so they sit next to each other and fold under if that parent is smaller than the combined with of the items.
Pretty standard.
This is to create a list of the twitter bootstrap icons, it gives something like this:
I have added next/previous keyboard navigation using the code below, however you will notice that the up/down arrow keys are mapped to call the left/right functions. What I have no idea how to do is to actually do the up/down navigation?
JsFiddle
(function ($) {
$.widget("ui.iconSelect", {
// default options
options: {
},
$select: null,
$wrapper: null,
$list: null,
$filter: null,
$active: null,
icons: {},
keys: {
left: 37,
up: 38,
right: 39,
down: 40
},
//initialization function
_create: function () {
var that = this;
that.$select = that.element;
that.$wrapper = $('<div class="select-icon" tabindex="0"></div>');
that.$filter = $('<input class="span12" tabindex="-1" placeholder="Filter by class name..."/>').appendTo(that.$wrapper);
that.$list = $('<div class="select-icon-list"></div>').appendTo(that.$wrapper);
//build the list of icons
that.element.find('option').each(function () {
var $option = $(this);
var icon = $option.val();
that.icons[icon] = $('<a data-class="' + icon + '"><i class="icon ' + icon + '"></i></a>');
if ($option.is(':selected')) {
that.icons[icon].addClass('selected active');
}
that.$list.append(that.icons[icon]);
});
that.$wrapper.insertBefore(that.$select);
that.$select.addClass('hide');
that._setupArrowKeysHandler();
that._setupClickHandler();
that._setupFilter();
that.focus('selected');
},
focus: function (type) {
var that = this;
if (that.$active === null || that.$active.length == 0) {
if (type == 'first') {
that.$active = that.$list.find('a:visible:first');
} else if (type == 'last') {
that.$active = that.$list.find('a:visible:last');
} else if (type == 'selected') {
that.$active = that.$list.find('a.selected:visible:first');
that.focus('first');
}
}
that.$active.addClass('active');
var toScroll = ((that.$list.scrollTop() + that.$active.position().top)-that.$list.height()/2)+that.$active.height()/2;
//that.$list.scrollTop((that.$list.scrollTop() + top)-that.$list.height()/2);
that.$list.stop(true).animate({
scrollTop: toScroll,
queue: false,
easing: 'linear'
}, 200);
if (type === 'selected') {
return false;
}
that.$select.val(that.$active.data('class'));
that.$select.trigger('change');
},
_setupArrowKeysHandler: function () {
var that = this;
that.$wrapper.on('keydown', function (e) {
switch (e.which) {
case that.keys.left:
that.moveLeft();
break;
case that.keys.up:
that.moveUp();
break;
case that.keys.right:
that.moveRight();
break;
case that.keys.down:
that.moveDown();
break;
case 16:
return true;
case 9:
return true;
break;
default:
that.$filter.focus();
return true;
}
return false;
});
},
_setupFilter: function(){
var that = this;
that.$filter.on('keydown keyup keypress paste cut change', function(e){
that.filter(that.$filter.val());
});
},
_setupClickHandler: function () {
var that = this;
that.$list.on('click', 'a', function () {
that.$wrapper.focus();
that.$active.removeClass('active');
that.$active = $(this);
that.focus('first');
});
},
moveUp: function () {
var that = this;
return that.moveLeft();
},
moveDown: function () {
var that = this;
return that.moveRight();
},
moveLeft: function () {
var that = this;
that.$active.removeClass('active');
that.$active = that.$active.prevAll(':visible:first');
that.focus('last');
return false;
},
moveRight: function () {
var that = this;
that.$active.removeClass('active');
that.$active = that.$active.nextAll(':visible:first');
that.focus('first');
return false;
},
filter: function(word){
var that = this;
var regexp = new RegExp(word.toLowerCase());
var found = false;
$.each(that.icons, function(i, $v){
found = regexp.test(i);
if(found && !$v.is(':visible')){
$v.show();
} else if(!found && $v.is(':visible')){
$v.hide();
}
});
}
});
})(jQuery);
Perhaps something like this: http://jsfiddle.net/QFzCY/
var blocksPerRow = 4;
$("body").on("keydown", function(e){
var thisIndex = $(".selected").index();
var newIndex = null;
if(e.keyCode === 38) {
// up
newIndex = thisIndex - blocksPerRow;
}
else if(e.keyCode === 40) {
// down
newIndex = thisIndex + blocksPerRow;
}
if(newIndex !== null) {
$(".test").eq(newIndex).addClass("selected").siblings().removeClass("selected");
}
});
Basically, you set how many items there are in a row and then find the current index and subtract or add that amount to select the next element via the new index.
If you need to know how many blocks per row there are, you could do this:
var offset = null;
var blocksPerRow = 0;
$(".test").each(function(){
if(offset === null) {
offset = $(this).offset().top;
}
else if($(this).offset().top !== offset) {
return false;
}
blocksPerRow++;
});
To deal with your 'edge' cases, you could do:
if(newIndex >= $(".test").length) {
newIndex = $(".test").length - newIndex;
}
moveUp: function () {
var that = this;
var index = $(this).index();
var containerWidth = parseInt( $('.select-icon-list').innerWidth(), 10);
var iconWidth = parseInt( $('.select-icon-list > a').width(), 10);
var noOfCols = Math.floor( containerWidth / iconWidth );
var newIndex = ( (index - noOfCols) < 0 ) ? index : (index - noOfCols);
var elem = $('.select-icon-list > a')[index];
},
Cache what ever remains static.
Im looking for a jQuery pluging (or code/guide) that does this: http://codecanyon.net/item/jquery-css3-sticky-mega-menu-bar/full_screen_preview/239093
This one is not free
Note: Notice the navigation bar is not placed at the top from the beginning. It sticks once the viewport "hits" it.
Anthony Garand also has a pretty sticky plugin - All free and actually maintained on GitHub:
jQuery Sticky Plugin
You could reverse engineer #genesis link which is the exact script you are looking for (no it is not hard)
I couldn't find a url for a non-minified version.. so here it is.
(function ($) {
$.fn.stickyMenubar = function (o) {
o = $.extend({
top: null,
floatingDiv: null,
floatingDivBackground: false,
megaMenu: true,
onScroll: function () {},
onLeaveTop: function () {},
sensitivity: 7,
padding: 5,
container_width: 960
}, o || {});
var setLiActions = function (t, lvl) {
var parent_ul = t.children('ul:first');
if (lvl > 0) {
t.addClass('inner_menu').hide();
} else {
if (!o.floatingDivBackground) {
t.addClass('smenubar_background');
}
}
t.mouseleave(function () {
$(this).children('li').children('ul').hide();
});
t.children('li').each(function () {
var li = $(this);
var class_parent = '';
var class_child = '';
var uls = li.children('ul');
if (uls.length) {
uls.each(function () {
setLiActions($(this), lvl + 1);
})
if (lvl == 0) {
li.children('a:first').addClass('arrow_down');
} else {
li.children('a:first').addClass('arrow_right');
}
if (!$.fn.hoverIntent) {
alert('hoverIntent javascript library must be included');
return;
}
var config = {
over: function () {
if (lvl == 0) {
$(this).parent().children('li').not($(this)).children('ul').hide();
var inner_ul = li.children('ul:first');
var pos = li.position();
var pos_ul = inner_ul.position();
var top_c = pos.top + li.height();
if (inner_ul.hasClass('show2left')) {
var left_c = pos.left - inner_ul.width() + li.width() + (o.padding * 2);
class_parent = 'inuseleft';
class_child = 'inusechildleft';
class_panel = 'left_slide';
inner_ul.addClass('topleftradius')
} else if (inner_ul.hasClass('mega_menu')) {
var left_c = 0 + ($(document).width() / 2) - (o.container_width / 2);
class_parent = 'inusemega', class_child = 'inusechildmega';
class_panel = 'mega_slide';
inner_ul.addClass('topleftradius').addClass('toprightradius');
} else {
var left_c = pos.left;
class_parent = 'inuse';
class_child = 'inusechild';
class_panel = 'right_slide';
inner_ul.addClass('toprightradius');
}
if (!$.browser.msie || ($.browser.msie && ($.browser.version > 7.0))) {
if (inner_ul.width() <= (li.width() + (o.padding * 2))) {
inner_ul.css('width', li.width() + (o.padding * 2));
inner_ul.removeClass('topleftradius').removeClass('toprightradius');
}
}
inner_ul.stop(true, true).css({
top: top_c,
left: left_c
}).addClass(class_panel).slideDown(150);
li.addClass(class_child);
} else {
var inner_ul = li.children('ul');
var pos = li.position();
var pos_ul = inner_ul.position();
var top_c = pos.top;
if (inner_ul.hasClass('show2left')) {
var left_c = pos.left - inner_ul.width() + li.width() - 1;
class_parent = 'inuseleft';
class_child = 'inusechildleft';
class_panel = 'left_slide';
} else {
var left_c = pos.left + li.width();
if ($.browser.mozilla) {
left_c--;
}
class_parent = 'inuse';
class_child = 'inusechild';
class_panel = 'right_slide';
}
inner_ul.css({
top: top_c,
left: left_c
}).addClass(class_panel).css({
'white-space': 'nowrap'
}).stop(true, true).animate({
width: 'toggle'
}, 300);
li.addClass(class_child);
}
},
sensitivity: o.sensitivity,
timeout: 200,
out: function () {
li.stop(true, true).removeClass(class_child).children('ul').hide();
}
};
li.each(function () {
$(this).hoverIntent(config);
$(this).mouseleave(function () {
if (lvl == 0) {
$(this).children('ul').hide();
}
});
});
} else {
li.mouseenter(function () {
li.stop(true, true).addClass(class_parent);
});
li.mouseleave(function () {
li.stop(true, true).removeClass(class_parent).children('ul').hide();
});
}
});
}
return this.each(function () {
var t = $(this);
t.addClass('smenubar');
if (!o.floatingDiv) {
var floatingDiv = t;
} else {
var floatingDiv = o.floatingDiv;
floatingDiv.css({
width: '100%'
});
}
var top_Y = 0;
if (!o.top) {
top_Y = floatingDiv.position().top;
} else {
top_Y = o.top;
}
if (o.floatingDivBackground) {
floatingDiv.addClass('smenubar_background');
t.css({
'border-bottom': 'none'
});
}
$(window).scroll(function () {
if ($(this).scrollTop() >= top_Y) {
if (!($.browser.msie && ($.browser.version <= 7.0))) {
floatingDiv.addClass('float_top');
o.onLeaveTop.call(this);
}
} else {
floatingDiv.removeClass('float_top').removeClass('float_top_ie7');
o.onScroll.call(this);
}
var open_menu = $('.inner_menu:visible');
var parent_li = open_menu.parent();
var pos = parent_li.position();
open_menu.css({
top: pos.top + parent_li.height()
});
});
t.find('.toggle_block').slideToggle();
t.find('.toggle_handle').html('...').css('text-align', 'center');
t.find('a').click(function () {
if ($(this).hasClass('toggle_handle')) {
var prev = $(this).parent().prev('.toggle_block');
prev.slideToggle().prevUntil(':not(.toggle_block)').slideToggle();
}
if ($(this).attr('href') == '#') {
event.preventDefault();
return;
}
});
if (o.megaMenu == true) {
setLiActions(t, 0);
}
});
};
})(jQuery);
play around with it. generally these plugins are very scale-able and customize-able so there is a bunch of junk code in there. The majority of what you want seems to be encased in
(window).scroll(function () {
if ($(this).scrollTop() >= top_Y) {
if (!($.browser.msie && ($.browser.version <= 7.0))) {
floatingDiv.addClass('float_top');
o.onLeaveTop.call(this);
}
} else {
floatingDiv.removeClass('float_top').removeClass('float_top_ie7');
o.onScroll.call(this);
}
var open_menu = $('.inner_menu:visible');
var parent_li = open_menu.parent();
var pos = parent_li.position();
open_menu.css({
top: pos.top + parent_li.height()
});
});