I have a list with scroll bar. Also there is a button that when pressed, it moves to a certain #id and scroll bar also moves to make that element visible. But it is not accurate. It moves, but not always to the exact place. How can I make this scroll function to be accurate:
DEMO http://jsfiddle.net/danials/anpXP/1/
My jQuery code:
function next() {
$(".list li").css("background", "grey");
goToByScroll(myID);
$("#" + myID).css("background", "red");
myID = $("#" + myID).next("li").attr("id");
}
function goToByScroll(id) {
$('.list').animate({
scrollTop: $("#" + id).offset().top - $("#" + id).height()
},
'slow');
}
In the demo try pressing the next button, and you'll see in some items the scroll moves but not correctly.
Any idea?
The problem with your code is that you are getting the offset of each element as you scroll down the list.
Offset is:
The .offset() method allows us to retrieve the current position of an element
relative to the document.
So this makes the offset of the box smaller, the further down the list you go.
What you need to do is figure out what the height+margin of an element is and do some math:
var myID = $(".list").children().first().attr("id");
function next() {
var li = $("#"+myID);
$(".list li").css("background", "grey");
var offset = parseInt(li.height())+parseInt(li.css("margin-top"));
$('.list').animate({scrollTop: offset*(myID-1)},'slow');
$("#"+myID).css("background", "red");
myID++;
}
This fiddle shows it in action. What it does is get the height+margin of the current element, and then multiplies it by how many elements down the list you are.
This only works assuming that all elements are the same size and that they have incremental IDs though.
UPDATE
If you want to make it work with Dynamic IDs, all you do is set an incremental variable to keep track of how many you have iterated through, and then grab the next ID similarly to how you did before:
var myID = $(".list").children().first().attr("id");
var inc = 1;
function next() {
var li = $("#"+myID);
$(".list li").css("background", "grey");
var offset = parseInt(li.height())+parseInt(li.css("margin-top"));
$('.list').animate({scrollTop: offset*(inc-1)},'slow');
$("#"+myID).css("background", "red");
myID = $("#"+myID).next().attr("id");
inc++;
}
And here's a fiddle.
Related
I have to create a very simple photo album in JQuery, with 4 pics and 2 buttons("previous" and "next")
Once I get to the end of the album (meaning I'm pressing "Previous" on the first image or "Next" on the last one) the script should simply go on showing me the various pics in loop.
I wrote the code for the "Next" button without issues, but using the same code (in reverse) for the "Previous" button just won't work: once I get to the first-child of my div, all I can see is an empty border.
<script>
$(document).ready(function () {
$("img").wrapAll("<div>");
$("div").children().hide();
var current = $("div :first-child");
current.show();
$("#nextbutton").click(function () {
if (current.is(":last-child")) {
current.hide();
current = $("div :first-child");
current.show();
}
else {
var next = current.next();
current.hide();
current = next;
current.show();
}
});
$("#prevbutton").click(function () {
if (current.is(":first-child")) {
current.hide();
current = $("div:last-child");
current.show();
}
else {
var prev = current.prev();
current.hide();
current = prev;
current.show();
}
});
});
</script>
<style>
div{border-color: yellow;
border-style: groove;}
</style>
Even stranger, instead of seeing my div border covering just the single image, I get a bigger one (specifically, the images are 8x8, the border is 8x20) and I don't know how to correct it as well.
There's a small typo in your code. In your $("#prevbutton").click(), the line:
current = $("div:last-child");
Should be:
current = $("div :last-child");
// ^space
$(div:last-child) is looking for the last child div whereas $(div :last-child) looks for the last child of that div.
JSFiddle
In the process of creating a tile-swap puzzle game for my jQuery class. Right now I'm working on trying to make the clicked tile switch with the blank tile (I'll figure out how to limit it to adjacent tiles afterwards). I've stored indexes of both indexes in new variables, but I can't figure out how to assign a variable as the td elements index.
$(document).ready(function(){
$('img').click(function(){
var tileSelected = $(this); //grab the clicked tiles index
var tileIndexOld = $("img").index(tileSelected);
var blankTile = $("#blank"); //grab the blank tiles index
var blankIndexOld = $("img").index(blankTile);
var tileIndexNew = blankIndexOld; //swap tile and blank indexes
var blankIndexNew = tileIndexOld;
$(this).attr("index", tileIndexNew);
$("#blank").attr("index", blankIndexNew);
});
});
I've also tried doing things like $(tileSelected).index(tileIndexNew); and $(this).index() = tileIndexNew; etc. I just can't seem to figure out how to overwrite with the new index.
Edit:
Okay, I've been shown the wickedness of my (attempted) index swapping ways! Still working on the solution, but I'm changing tracks and focusing on altering the src's as suggested by Starscream1984. I'll update again once I've got it figured out, many thanks to all!
Solution:
After trying it three different ways (with multiple sub-variations) this is what I ended up with:
$(document).ready(function(){
$("td").click( function(){
var tileVertical = $(this).index(); //get clicked tiles vertical position via its td
var tileHorizontal = $(this).parent().index(); //get clicked tiles horizontal position via its tr
var blankTile = $("#blank").parent(); //getting the td that contains the blank tile
var blankVertical = blankTile.index(); //get blank tiles vertical position (via its td)
var blankHorizontal = blankTile.parent().index(); //get blank tiles horizontal position via its tr
if( Math.abs(blankVertical - tileVertical) + Math.abs(blankHorizontal - tileHorizontal) == 1) //check if clicked tile is adjacent to the blank tile
{
blankTile.empty().html( $(this).html() ); //put the tile html into the blank slot
$(this).html("<img id='blank' src='blank.jpeg' width='200px' />"); //fill the tile slot with the blank, ID IS CRITICAL!!!!
} //function will only run once if id is omitted from this tag!!!
return 1;
});
});
My original approach tried to use the index as a quick and dirty variable to swap out. What I discovered was that the index in this case is simply more like a map with x and y coordinates. It was the inner html of the table cell that needed to be swapped, not the index itself.
You need to move the elements around using jQuery DOM modification methods. This solution assumes that each tile is contained in a DIV in the grid.
$('img').click(function(){
var tileSelected = $(this);
var parentSelected = tileSelected.parent();
var blankTile = $("#blank");
var blankParent = blankTile.parent();
parentSelected.append(blankTile);
blankParent.append(tileSelected);
});
A)
$('img').each(function(index, elem){
console.log(index);
});
B)
$('img').click(function(){
var index = $(this).index();
//or
//var index = $('img').index($(this));
console.log(index);
});
Are the right ways. So your code seems to be correct. Are you sure that all imgages exists at the moment you run the function? Do you noticed that your selector find all img in DOM?
If you want to assign an index variable for the td, one approach to do it dinamically is the following:
$(document).ready(function(){
var tdArray = $("td");//grabs all td elements
for(i = 0; i < tdArray.length; i++){
$(tdArray[i]).attr('data-index',i);//adds a data-index attribute on each one
}
});
You could then handle a click event like this:
$("td").click(function(){
alert($(this).attr('data-index'));
});
To keep track of your blank tile, you could simple assign its data-index to a global variable.
I'm trying to pin some divs in place and fade them in and out as a user scrolls down. My code looks like this so far:
$(window).on("load",function() {
var fadeDuration = 500;
function fade() {
// compute current window boundaries
var windowTop = $(window).scrollTop(),
windowBottom = windowTop + $(window).innerHeight(),
focusElt = null;
// find our focus element, the first visible .copy element,
// with a short-circuiting loop
$('.imgdiv').toArray().some(function(e, i) {
var objectTop = $(e).offset().top;
if ((objectTop >= windowTop) && (objectTop <= windowBottom)) {
focusElt = e;
return true;
}
console.log(focusElt);
});
// obscure all others
$('.focus').not(focusElt)
.removeClass('focus')
.fadeTo(fadeDuration, 0);
// focus on our focus element; if was the previous focus, nothing
// to do; but if it wasn't focus / wasn't showing before, make
// it visible and have class focus
$(focusElt).not('.focus')
.addClass('focus')
.fadeTo(fadeDuration, 1);
}
fade(); //Fade in completely visible elements during page-load
$(window).scroll(function() {fade();}); //Fade in elements during scroll
});
Here's the corresponding fiddle that almost does what I'm looking for, but instead of the green "Fade In" blocks moving upward and fading, I want them pined in place near the top of the window. As the "IMG DIVs" move past them they will fade and reappear with each new "IMG DIV". Here, I'm focusing on the particular green block and fading it when it becomes the focus element. Instead, what I need to do is, focus on the IMG DIV blocks, add a "pinned" class to the green blocks when they reach the top of the page, and fade the green blocks in and out.
Does anyone have any advice?
Part 2 of my question is how to do this with native JavaScript, and not rely on jQuery's dependency.
Ok, so lets split your first issue into two issues :)
First of all, you want to (in general) do something when some element becomes visible in the viewport and when it becomes invisible. So, basically, all you need is function like that:
watchElementIsInViewport(
$('.imgdiv'),
doSomethingWhenElementAppearedInViewport,
doSomethingWhenElementOutOfViewport
);
You know, that when element becomes visible, you want to show some other element. When element becomes invisible, you want to hide that related element. So now, define those two functions:
function doSomethingWhenElementAppearedInViewport(element) {
// retrieve text related with the element
var $copy = $(element).next('.copy');
// fade it in
$copy.fadeTo(500, 1);
}
function doSomethingWhenElementGotOutOfViewport(element) {
// retrieve text related with the element
var $copy = $(element).next('.copy');
// fade it out
$copy.fadeTo(500, 0);
}
What about watchElementIsInViewport? There is no magic inside, only logic you already created, but decoupled from showing of finding elements.
function watchElementIsInViewport($elements, elementAppearedInViewport, elementGotOutOfViewport) {
var currentlyVisible = [ ];
// retrieve positions once, assume it won't change during script is working
var positions = getVerticalBoundaries($elements);
function _scrollHandler() {
var viewportTop = window.scrollY;
var viewportBottom = viewportTop + window.innerHeight;
$elements.each(function(index, element) {
var elementPosition = positions[index];
/* if you wish to check if WHOLE element is in viewport
* var elementIsInViewport = (elementPosition.top >= viewportTop) &&
* (elementPosition.bottom <= viewportBottom);
*/
var elementIsInViewport = (elementPosition.top < viewportBottom) &&
(elementPosition.bottom > viewportTop);
var elementIndexInCurrentlyVisible = currentlyVisible.indexOf(element);
// if element is visible but was not visible before
if(elementIsInViewport && (elementIndexInCurrentlyVisible === -1)) {
elementAppearedInViewport(element);
currentlyVisible.push(element);
// if element is not visible but was visible before
} else if(!elementIsInViewport && (elementIndexInCurrentlyVisible !== -1)) {
elementGotOutOfViewport(element);
currentlyVisible.splice(elementIndexInCurrentlyVisible, 1);
}
});
}
// initial check & update
_scrollHandler();
// check & update on every scroll
$(window).on('scroll', _scrollHandler);
}
And that's all. Working example.
I made a function that Is made to be trigered when user scrolls on a element on the page. In this case when user scrolls to an id then it fades in. The problem is that they fade in all at the same time with the first scroll instead of when they reaching the element That is supposed to allow it to fade in! Please help me make my function work.
Thanks a lot
var selected={
//// Storing selectors
items:[],
/// Function that stores items and hides them from the page
selectFunc: function(select) {
//// Store selected element
selected.items.push(select);
/// hide selector from the page
$(select).hide();
}
};
//// Function triggeres on scroll
$(window).scroll(function() {
/// loops trough the selected elements
for(i=0; i<selected.items.length; i++){
var currentItem = selected.items[i];
///// calculates your position and item position
var hT = $(currentItem).offset().top,
hH = $(currentItem).outerHeight(),
wH = $(window).height(),
wS = $(this).scrollTop();
////// check if you are in the position
if (wS > (hT+hH-wH)){
$( currentItem ).fadeIn( 2500 );
}
}
});
//// Using my function to select id about and p element in it.
selected.selectFunc("#about p");
selected.selectFunc("#about input");
In your for loop, you are doing an iteration for each element in selected.items. What's in there? Two strings: "#about p", and "#about input".
So for each of these selectors, you show them all. You need to get every element separately.
Another problem is that hiding these elements means they are not taking up the space they should on the page, so you might not be able to scroll down. You can solve that by changing their opacity instead of making them display:none (what .hide() is doing).
Here is your code with some modifications:
var selected = {
//// Storing selectors
items: [],
/// Function that stores items and hides them from the page
selectFunc: function(select) {
//// Store selected element
var items = $(select);
for (var i = 0, l = items.length; i < l; i++) selected.items.push(items[i]);
/// hide selector from the page
items.css('opacity', 0);
}
};
//// Function triggeres on scroll
$(window).scroll(function() {
/// loops trough the selected elements
for (i = 0; i < selected.items.length; i++) {
var currentItem = selected.items[i];
///// calculates your position and item position
var hT = $(currentItem).offset().top,
hH = $(currentItem).outerHeight(),
wH = $(window).height(),
wS = $(this).scrollTop();
////// check if you are in the position
if (wS > (hT + hH - wH)) {
$(currentItem).animate({
'opacity': 1
}, 2500);
}
}
});
//// Using my function to select id about and p element in it.
selected.selectFunc("#about p");
selected.selectFunc("#about input");
// Simulating a scroll to show the first elements
$(window).scroll();
JS Fiddle Demo
So, I need a div to slide up when another slides down.
Example:
When Home button is clicked a div, we'll call it box_Home, slides down. When Games button is clicked, box_Home should slide up and then box_Games should slide down. What's happening is that they are overlapping instead of swapping out.
http://jsfiddle.net/M8UgQ/15/
var open = $('.open'),
a = $('ul').find('a');
console.log(a.hasClass('active'));
open.click(function(e) {
e.preventDefault();
var $this = $(this),
speed = 500;
var link_id = $this.attr('id');
var box_id = '#box_' + link_id;
console.log(box_id);
if($this.hasClass('active') === true) {
$this.removeClass('active');
$(box_id).slideUp(speed);
} else if(a.hasClass('active') === false) {
$this.addClass('active');
$(box_id).slideDown(speed);
} else {
a.removeClass('active')
$(box_id).slideUp(speed);
$this.addClass('active');
$(box_id).delay(speed).slideDown(speed);
}
});
take a look at this
http://jsfiddle.net/rWrJ9/1/
the main idea is...
if the element clicked is active, remove it, otherwise: 1. find (if any) already active elements (using $('.active')) and use jQuery.map() to make them inactive and slide them up, and 2. make the element clicked active.
I also removed the unneeded variable a
IMPORTANT: the this inside the map() function is different from the this (or rather, $this as you called it) outside the map() function
I think you're saying you have two buttons id="Home" class="open" and id="Game" class="open", and two divs id="box_Home" and id="box_Game". If so, you add class="box" to box_Home and box_Game and do something like this:
$('.open').click(function(e) {
e.preventDefault();
var $this = $(this);
var link_id = $this.attr('id');
var box_id = '#box_' + link_id;
$('.box').slideUp();
$(box_id).slideDown();
});
Hi check this fiddle i hope you need thing to implement
jsfiddle
in the if else statement you are doing a mistake
else if(a.hasClass('active') === false) {
replace it with
else if($this.hasClass('active') === false) {