Here i have created a image slider kind of thing with 3 images initially, where clicking on pre/next button will replace the prev next image.
I want clicking on prev/next button should completely replace the images with prev/next 3 images(not just one).
var stPt = 0, elToShow = 10; //showing 10 elements
var $ul = $('ul.ice-navigator');
var $li = $('ul.ice-navigator li'); //get the list of li's
var $copy_li = [];
var copy_lgt = $li.length - elToShow;
//call to set thumbnails based on what is set
initNav();
function initNav() {
var tmp;
for (var i = elToShow; i < $li.length; i++) {
tmp = $li.eq(i);
$copy_li.push(tmp.clone());
tmp.remove();
}
}
$('.ice-next').click (function () {
$li = $('ul.ice-navigator li'); //get the list of li's
//move the 1st element clone to the last position in copy_li
$copy_li.splice(copy_lgt, 0, $li.eq(0).clone() ); //array.splice(index,howmany,element1,.....,elementX)
//kill the 1st element in the UL
$li.eq(0).remove();
//add to the last
$ul.append($copy_li.shift());
});
$('.ice-previous').click (function () {
$li = $('ul.ice-navigator li'); //get the list of li's
//move the 1st element clone to the last position in copy_li
$copy_li.splice(0, 0, $li.eq(elToShow-1).clone()); //array.splice(index,howmany,element1,.....,elementX)
//kill the 1st element in the UL
$li.eq(elToShow-1).remove();
//add to the last
$ul.prepend($copy_li.pop());
});
http://jsfiddle.net/devsvits/hV5DA/
How can i do this?
Here is one way of doing it...
You stick your advance code in a function and call it as many times as images you want to advance.
http://jsfiddle.net/hV5DA/1/
$('.ice-next').click (function () {
function nextItem(){
$li = $('ul.ice-navigator li'); //get the list of li's
//move the 1st element clone to the last position in copy_li
$copy_li.splice(copy_lgt, 0, $li.eq(0).clone() ); //array.splice(index,howmany,element1,.....,elementX)
//kill the 1st element in the UL
$li.eq(0).remove();
//add to the last
$ul.append($copy_li.shift());
}
//number of items to replace
var numItemsToAdvance = 3;
//call function n times
while(numItemsToAdvance--){
nextItem();
}
});
Related
I have a Table of Contents with id #TableOfContents in which each href points to a h2 or h3.
The problem I am having is that once the Heading, h2 or h3 is observed by intersection observer, Entry for that is highlighted by adding class side-active for that link in #TableOfContents, but as soon as the long content (such as p paragraph) after the heading comes in viewport the highlight for that section is removed since the heading is not in viewport.
This is a problem since I want the section (h2, h3) to still be highlighted until next h2 or h3 doesn't cross half of viewport.
What can I do?
window.addEventListener('DOMContentLoaded', () => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const id = entry.target.getAttribute('id');
if (entry.intersectionRatio > 0) {
document.querySelector(`#TableOfContents a[href="#${id}"]`).classList.add('side-active');
} else {
document.querySelector(`#TableOfContents a[href="#${id}"]`).classList.remove('side-active');
}
});
});
toc = document.querySelectorAll('#TableOfContents a');
// get content so that link refer to it
toc.forEach(function (link) {
var id = link.getAttribute("href");
var element = document.querySelector(id);
observer.observe(element);
});
});
Text is highlighted when heading in viewport
Text is not highlighted once heading is not in viewport
So the thing was we needed to calculate containerBottom by:
adding current container's top + next container's top
Or if it is last container then,
current container's top + it's outerHeight()
Once that; if ith container is in scrollposition add class to it And remove class from all containers previous to ith container (line no 18-21) (reverse loop)
$(function () {
// Table of contents (`ul`) that contains `a` tag in `li` which we want to highlight
var sectionIds = $('#TableOfContents a');
$(document).on('scroll', function(){
sectionIds.each(function(i, e){
var container = $(this).attr('href');
var containerOffset = $(container).offset().top; // container's top
var nextContainer = $(sectionIds[i+1]).attr('href')
if (i != sectionIds.length-1) {
// if this container isn't last container
var containerHeight = $(nextContainer).offset().top;
} else {
// last container's height will be outerHeight
var containerHeight = $(container).outerHeight();
}
var containerBottom = containerOffset + containerHeight;
var scrollPosition = $(document).scrollTop();
if(scrollPosition < containerBottom - 20 && scrollPosition >= containerOffset - 20){
for (var j = i; j >= 0; j--) {
$(sectionIds[j]).removeClass('active');
}
$(sectionIds[i]).addClass('active');
} else{
$(sectionIds[i]).removeClass('active');
}
});
});
});
Check the first heading ("Line Equations" h1) that is highlighted stays highlighted even though the title is not in viewport until next sub heading ("Distance of a point from a line") comes into view.
I have a class which creates mousedown listeners for each LI. However, a click to the LI of the first instance results in events firing for every LI instance which was created after it. If you create a 3rd instance, the click on the 1st instance will result in all 3 firing and a click on the 2nd results in the 2nd and 3rd firing, etc.
To duplicate, run the jsfiddle and click on the two links (one after the other) which will create two UL elements each containing an LI element. Alerts() are set up to show each event firing. If you continue to click on the links, you will see how the events continue to stack.
I'm new to creating classes in javascript, so I've obviously missed something, but can't see why the events for the multiple instances are not separate. In case you're wondering, I've created my class the way I did to allow for static variables and methods. I am not using jQuery.
I've posted a jsfiddle of the below code
UPDATE
Fixed errors due to paring complicated code down for SO. I've updated to reflect multiple LI elements being created in each UL (the number is dynamic and db driven) and the ID's being unique.
<div id="outer">
Click here first id=g123
<br>
Click here second id=g234
</div>
<script>
listbox = (function (id) {
var currentul = "";
var counter = 0;
function listbox(id) {
this.id = id;
};
// show or hide once all the properties are set
listbox.prototype.go = function () {
this.initlistbox();
this.addlisteners();
}
// create the elements for drag and drop between two list boxes
listbox.prototype.initlistbox = function () {
var _this = this;
var divlist = document.createElement('div');
divlist.style.minHeight = '100px';
divlist.style.width = '300px'
divlist.style.outline = '1px solid blue'
// the UL
var ul = document.createElement('ul');
ul.setAttribute('id', 'ulchosen-' + this.id);
// track which UL is active for keystrokes
ul.addEventListener('click', function (e) {
if (e.stopPropagation) e.stopPropagation();
currentul = this.id;
}, false);
// add the ul to the div
divlist.appendChild(ul);
divouter = document.getElementById("outer");
// add the div to the body
divouter.appendChild(divlist);
// The LI
// create LI elements
for (i=0; i < 4; i++){
var li = document.createElement('li');
counter++;
li.setAttribute('id', 'lb-li-chosen-' + counter);
li.innerHTML = "Hello " + counter + " " + this.id;
ul.appendChild(li);
}
};
listbox.prototype.addlisteners = function () {
var _this = this;
var lis = document.querySelectorAll("[id^='lb-li-']");
for (var i = 0; i < lis.length; i++) {
this.setlilisteners(lis[i]);
}
};
listbox.prototype.setlilisteners = function (el) {
var _this = this;
// click is for non drag, non shift / ctrl
el.addEventListener('click', function (e) {
alert("A: " + _this.id);
e.preventDefault();
return false;
}, false);
// mousedown is to select LI elements
el.addEventListener('mousedown', function (e) {
e.stopPropagation();
e.preventDefault();
alert("B: " + _this.id);
return false;
}, false);
};
return listbox;
})();
function edit(id) {
var a = new listbox(id);
a.go();
}
</script>
Each time you use addEventListener a new function is stacked for execution. addEventListener does not removes the older function. So you have to call it just once per element.
The code below will work.
listbox = (function (id) {
var currentul = "";
function listbox(id) {
this.id = id;
};
// show or hide once all the properties are set
listbox.prototype.go = function () {
this.initlistbox();
}
// create the elements for drag and drop between two list boxes
listbox.prototype.initlistbox = function () {
var _this = this;
var divlist = document.createElement('div');
divlist.style.minHeight = '100px';
divlist.style.width = '300px'
divlist.style.outline = '1px solid blue'
// the UL
var ul = document.createElement('ul');
ul.setAttribute('id', 'ulchosen-' + this.id);
// track which UL is active for keystrokes
ul.addEventListener('click', function (e) {
if (e.stopPropagation) e.stopPropagation();
currentul = this.id;
}, false);
// add the ul to the div
divlist.appendChild(ul);
divouter = document.getElementById("outer");
// add the div to the body
divouter.appendChild(divlist);
// The LI
// create LI elements
var li = document.createElement('li');
li.setAttribute('id', 'lb-li-chosen-1');
li.innerHTML = "Hello " + this.id;
ul.appendChild(li);
this.setlilisteners(li);
};
listbox.prototype.setlilisteners = function (el) {
var _this = this;
// click is for non drag, non shift / ctrl
el.addEventListener('click', function (e) {
alert("A: " + _this.id);
e.preventDefault();
return false;
}, false);
// mousedown is to select LI elements
el.addEventListener('mousedown', function (e) {
e.stopPropagation();
e.preventDefault();
alert("B: " + _this.id);
return false;
}, false);
};
return listbox;
})();
function edit(id) {
var a = new listbox(id);
a.go();
}
Gustavo's answer helped me locate the bug. I wasn't filtering the LI elements to only include the current UL, so with each additional instance, a listener for ALL the LI elements were being set again. He was trying to tell me this... So here it is:
listbox.prototype.addlisteners = function () {
var _this = this;
var lis = document.querySelectorAll("[id^='lb-li-']");
for (var i = 0; i < lis.length; i++) {
// THIS IF STATEMENT WAS MISSING
if (lis[i].parentNode.id.indexOf("-" + this.id) !== -1){
this.setlilisteners(lis[i]);
}
}
};
The fiddle is creating multiple LI elements with the same id lb-li-chosen-1 which is invalid HTML. In some browsers I believe it may result in the query selector returning an array of multiple LI elements with result that each time .addListeners is called a new event listener function is added to each LI element defined so far.
Want to display 3 items from ul at a time. And when user clicks to the next arrow each element is traversed and when control reaches to last item i.e 3rd item , next three items should be shown in place of first three items.
The list should not be circular, means after last item in the list there must not be first item.
Have created fiddle but it traversing in circular fashion.
I am traversing list using this,
$("#rightArrow").click(function (e) {
var curr = $("#itemsListBox ul li:last");
curr.parent().prepend(curr);
});
$("#leftArrow").click(function (e) {
var curr = $("#itemsListBox ul li:first");
curr.parent().append(curr);
});
http://jsfiddle.net/N6XrL/
You could simply hide and show the appropriate element instead of appending/prepending:
http://jsfiddle.net/N6XrL/2/
var $elements = $("#itemsListBox ul li");
var count = $elements.length;
var showNum = 3;
var counter = 0;
$("#rightArrow").click(function (e) {
if (counter + showNum < count) {
counter++;
}
display();
});
$("#leftArrow").click(function (e) {
if (counter > 0) {
counter--;
}
display();
});
function display() {
$elements.hide();
$elements.slice(counter, counter + showNum).show();
}
display();
You should keep track of you current picture. like in following code.
var items_count = $("#itemsListBox ul li").length, current_item_counter=0;
$("#rightArrow").click(function(e) {
if(current_item_counter<items_count)
{
var curr = $("#itemsListBox ul li:last");
curr.parent().prepend(curr);
current_item_counter++;
}
});
$("#leftArrow").click(function(e) {
if(current_item_counter> 0)
{
var curr = $("#itemsListBox ul li:first");
curr.parent().append(curr);
current_item_counter--;
}
});
here is jsFiddle.
I created a menu with Jquery FadeIn animation, i want to open the menu when my mouse is hover but i also want to fadein the previous tab content.
This should works like this :
My mouse is one the third tab, the first tab popin, then the second one, then the third with a little delay between each animation.
I tried to do this with Jquery :
$('.navigation li').hover(
// When mouse enters the .navigation element
function () {
var tab = $(this).attr('id');
var numTab = tab.substring(2);
//Fade in the navigation submenu
for ( var i = 0; i <= numTab ; i++ ) {
var domElt = '#Et' + i + ' ul';
$(domElt).fadeIn(300); // fadeIn will show the sub cat menu
console.log(domElt);
}
},
// When mouse leaves the .navigation element
function () {
var tab = $(this).attr('id');
var numTab = tab.substring(2);
//Fade out the navigation submenu
for ( var i = 0; i <= numTab ; i++ ) {
var domElt = '#Et' + i + ' ul';
$(domElt).fadeOut(); // fadeIn will show the sub cat menu
}
}
);
As you see on the live version of it, it don't really works, all the tabs are fadein together, or seems to. How can i add a delay to get the "one-after-one fadein effect"?
Add dynamic delay like this -
$(domElt).delay(i*100).fadeOut();
Demo ---> http://jsfiddle.net/abJkD/2/
You can chain the fades:
function () {
var tab = this.id;
var numTab = +(tab.substring(2));
//Fade in the navigation submenu
var i = 0;
function doFade() {
if (i === numTab) return;
// In the fiddle provided, the element id values
// start at 1, not zero
var domElt = '#Et' + (i + 1) + ' ul';
i = i + 1;
$(domElt).fadeIn(300, doFade);
}
doFade();
},
(and similarly for the fade out)
I'm animating a div to left by 0px by clicking on the div colored in red. Below the div , classes are added to li's as the div moves along, but the classes gets added to only certain li's and not all.
Is there any other logic to fix this ?
Fiddle - http://jsfiddle.net/AsfFQ/16/
Below is the image of the issue
Try this jsFiddle example.
var pos;
var timer, selectLi = (function() {
var $block = $('.block'),
$container = $('.container'),
$lis = $('.container ul li'),
liWidth = $lis.width(),
$selectedLi;
return function() {
pos = $block.offset().left - $container.offset().left;
liNum = Math.round(pos / liWidth);
// $selectedLi && $selectedLi.removeClass('selected');
$selectedLi = $($lis.get(liNum));
$('li.eligible').each(function() {
if ($block.offset().left-3 <= $(this).offset().left) $(this).addClass('selected');
});
};
})();
$('.block').click(function() {
timer = setInterval(selectLi, 30);
$(this).animate({
left: 0
}, function() {
clearInterval(timer);
});
});
$('li').each(function() {
$(this).addClass('eligible');
if ($(this).offset().left > $('.block').offset().left) $(this).removeClass('eligible');
});
This sets the eligible list items and then as the bar moves, compares their position to tjat of the bar and if they're in range, they get the class added.
Your little animation needs only a little code.
See jsfiddle example
var $block = $('.block'),
start = $block.offset().left;
$block.one('click').animate({left: 0})
.$('li').filter(function(){return $(this).offset().left<=start})
.repeat(30).filter(function(){return $(this).offset().left>=$block.offset().left})
.addClass('selected').unrepeat();
I'm using this plugin jquery-timing.
This also works when animating 100px on each click, see another fiddle:
var $block = $('.block');
$block.on('click').animate({left: '-=100px'})
.$('li').filter(function(){return $(this).offset().left<=$block.offset().left})
.repeat(30).filter(function(){return $(this).offset().left>=$block.offset().left})
.addClass('selected').unrepeat();
Have fun!