I have the following html structure repeated multiple times on a page:
<div class="item">
<div class="header">
...
Close All Expanded
</div>
<div class="expanded">
...
</div>
</div>
And some jQuery to close all the divs with class expanded when the link is clicked:
$('.closeExpanded').click(function(e){
e.preventDefault();
$('.expanded').slideUp('slow');
});
However I want to ensure that the link you've just clicked remains in view and moves as little as possible. Currently clicking on a link halfway down the page causes the link to move up out of the viewport as divs above it are closed.
Is there a nice graceful way I can keep the link that's been clicked in the viewport?
Update:
I've tried the answers suggested so far but so far none completely work (e.g. clicking link number 30 in each of these leads to link number 30 ending up outside of the viewport)
mrtsherman's solution: http://jsfiddle.net/Qan5p/38/
Mohsen's solution: http://jsfiddle.net/Qan5p/39/
roXon's solution: http://jsfiddle.net/Qan5p/40/
You will need to modify the scrollTop property of the page to keep things in place. Fortunately, as elements are shrunk they will be triggering scroll events you can hook into.
//untested, but should look something like this
var linkPosition = null;
$('.closeExpanded').click(function(e){
e.preventDefault();
linkPosition = $(this).offset().top - $(document).scrollTop();
//in callback to slideUp clear linkPosition so that we know to stop tracking scroll events
$('.expanded').slideUp('slow', function() {
linkPosition = null;
});
});
$(document).scroll( function(){
//check to see if we should be keeping link on screen
if (linkPosition != null) {
//keep the link in position
//I'm not so sure about this bit of the code, but I think you get the idea. All you have to do
//is properly calculate the new offset to keep the link looking like it is in the same position
var newPos = $(document).scrollTop() + linkPosition;
$(document).scrollTop(newPos);
}
});
$('.closeExpanded').click(function(e){
e.preventDefault();
$('.expanded').css({
'position' : 'absolute', // make it position absolute to prevent moving
'left' : $(this).offset().left,
'top' : $(this).offset().top
}).slideUp('slow', function(){
$('.expanded').css('position', 'static');
});
});
Fiddle: http://jsfiddle.net/mohsen/Qan5p/10/
WORKING DEMO
The easiest way:
Wrap contents into dynamically generated divs.
First animate the contents,
Than animate the wrapper elements
$('.expanded').wrapInner('<div class="wrapper" />');
$('.expanded').each(function() {
$(this).height($(this).children('.wrapper').height());
});
$('.closeExpanded').click(function(e) {
e.preventDefault();
$('.wrapper').animate({height: '0px'}, 800, function() {
$('.expanded').slideUp(800);
});
});
Related
I am trying to implement two static buttons for navigating up or down between about 10 containing div tags on a single fairly deep page of content.
I want the buttons to smoothly scroll to the next part of the page (next containing div) whenever they are clicked on.
The problem with this solution is that if you manually scroll up and down the page using the browser scroll bar or the mouse wheel then the logic of the code is not aware of this and when you next click next/prev a scroll takes place that is not actually relevant to the viewable area you see, totally ruining the user experience.
You can test this in this demo: http://jsfiddle.net/aVJBY/ . If you click NEXT once it works. Now scroll down to near the bottom of the content and click PREV. In theory the page should go one step back from the bottom of the page. Instead it returns to the top of the page.
Maybe I just need to scrap this code and use some external library which is fine, but I can't find anything appropriate. Anyone have an idea on how to make my code resolve this issue?
The code I am using so far is here:
$('div.section').first();
$('a.display').on('click', function(e) {
e.preventDefault();
var t = $(this).text(),
that = $(this);
if (t === 'next' && $('.current').next('div.section').length > 0) {
var $next = $('.current').next('.section');
var top = $next.offset().top;
$('.current').removeClass('current');
$('body').animate({
scrollTop: top
}, function () {
$next.addClass('current');
});
} else if (t === 'prev' && $('.current').prev('div.section').length > 0) {
var $prev = $('.current').prev('.section');
var top = $prev.offset().top;
$('.current').removeClass('current');
$('body').animate({
scrollTop: top
}, function () {
$prev.addClass('current');
});
}
});
I resolved this with the wonderful jQuery inview plugin - https://github.com/protonet/jquery.inview An overview of what I did follows...
I first setup some variables, the pageItems array contains all the divs I need to monitor...
var posNext=0;
var posPrev=0;
var pageItems = ["pageone", "pagetwo", "pagethree", "pagefour", "pagefive"];
I then setup the following in document ready. Thanks to the inview plugin and the jQuery bind event, on a page scroll (of any kind, either by my buttons, manually or via mouse wheel) the plugin is run. I first search the array of page items for a match with what is returned by the $(this).attr("id") value. I then adjust the posNext/posPrev variables with values based on the current div in view.
$(document).ready(function (){
$(".divclass").bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
if (isInView) {
matchPos = pageItems.indexOf($(this).attr("id"));
// Determine prev/next positions now we have an index. The position values used in click events later
if ( (matchPos+1)==pageItems.length ){
posNext=matchPos;
posPrev=matchPos-1;
}else if (matchPos==0){
posNext=matchPos+1;
posPrev=0;
}else{
posNext=matchPos+1;
posPrev=matchPos-1;
}
} else {
// dont update index
}
});
});
Finally also within document.ready I have binds to catch clicks on the buttons I have on screen all the time. These use a jQuery animate call to scroll to the div id value specified via the array index values in posNext/posPrev.
$(".down-button").click(function(e){
event.preventDefault();
$('html, body').stop().animate({
scrollTop: $("#"+pageItems[posNext]).offset().top
}, 500);
});
$(".up-button").click(function(e){
event.preventDefault();
$('html, body').stop().animate({
scrollTop: $("#"+pageItems[posPrev]).offset().top
}, 500);
});
Consider the following snippet:
<div id="help"></div>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
<script>
var loadPage = function (){
$("#help").load("http://localhost:3000/manual.html");
}
onload=loadPage;
</script>
This exists on my main page:
http://localhost:3000/
The above code works fine and loads my manual page. But if I click a link like this in manual.html:
<a href='#introduction'>Introduction</a>
Then the page in the help div jumps to the #introduction section, however the url in my browser updates to:
http://localhost:3000/#introduction
This is pointless because the #introduction anchor only exists in manual.html, how can I prevent the links in the #help div from affecting the address bar in the browser?
Try this
$('a').click(function(e) {
e.preventDefault();
$("#help").load($(this).attr('href'));
})
By using offset and preventDefault
$('a').click(function(e) {
// Go to '#introduction'
var targetId = $(this).attr('href');
$('html, body').offset({ top: $(targetId).offset().top, left: 0 });
// this prevent 'http://localhost:3000/#introduction'
e.preventDefault();
});
See this post
I already had a function that could scroll the #help window to a heading, when you click on objects in the parent it scrolls to the relevant section in the help window:
var moveTo = function(destination){
//position of the top of the help window - does not change.
var helpWindow = $("#help").offset().top;
//difference between current scroll position and target position.
var relativeDistance = $(destination).position().top - helpWindow;
//add the distance from current position to the top to get distance to target from top
var absoluteDistance = relativeDistance+ $("#help").scrollTop();
$("#help").animate({
scrollTop: absoluteDistance
}, 1000);
}
Using e.preventDefault() I was able to use this function to do what I want.
For others heading down this path there are two other small things to consider.
Firstly, make sure you nest the .click() function inside the callback from page load, as the hyper links won't exist until the page is loaded. Secondly, You will probably want to use a child selector eg $('#help a').click() to ensure you are only altering the behaviour on links inside the child.
$("#help").load("http://localhost:3000/manual.html", function(){
$('#help a').click(function(e) {
e.preventDefault(); //suppress standard link functionality
moveTo($(this).attr('href')); //scroll to link instead.
})
});
I am new to jQuery, I built this page and what I would like to happen is when a block e.g. 2011 1 October reaches the top of the page it displays the content for that specific div via the id or date-attr.
When you click on a date it will display the content for that date but I would like it to appear once that date block reaches the top of the page.
I have looked around the net but no luck thus far.
Use scroll event to make checks for divs you are interested in - assing class for them for instance.
http://api.jquery.com/scroll/
In that event callback you can check each elements position
http://api.jquery.com/each/
To determine position element on page use
http://api.jquery.com/offset/ - top component
But don't take this value - you need to substract Window scroll position which is returned by
$(window).scrollTop()
And make some border values when element should be opened and when closed.
As per your description that I understood, you will have to add one line of code to your "main.js" file.
// javascript + jquery scripts
// script for fading in content boxes -->
$(".square").on("click", function() {
var id= $(this).attr("contentId");
//$("#details");
$('#details').fadeOut('slow', function() {
$(this).html($("#" + id).html()).fadeIn('fast');
});
});
// active link -->
$(".square").click(function() {
$(".square").removeClass("active");
$(this).addClass("active");
$("html, body").scrollTop($(this).position().top); // THIS IS THE LINE TO BE ADDED TO SCROLL TO THE CURRENT DATE ITEM
});
// fade in main content div / intro
$(document).scroll(function () {
var y = $(this).scrollTop();
if (y > 10) {
$('.topBlock').fadeOut();
} else {
$('.topBlock').fadeIn();
}
});
// hide intro text onClick on list
$( "li" ).click(function() {
$( ".topBlock" ).hide().animate();
});
var viewportHeight = $(window).height();
//$j(".parallax_section_holder").css("height",$j(window).height()-116);
//alert(viewportHeight);
I hope I have solved your issue, and if not, then please provide me the exact code link in jsfiddle.
Regards.
I'm trying to make a single down arrow that jumps to the next ID on the page as you scroll down. I don't really know JavaScript so I'm trying to keep it as simple as possible. I thought, as there are only a few sections, that I could just hide and display different divs with arrows that have different targets. I used two different codes to arrive at this, but doesn't seem to be working. Any ideas?
<script type="text/javascript">
$(window).scroll(function(){
if($(window).scrollTop() >= 800) {
var elem = document.getElementById("arrow");
elem.setAttribute("style","display:none;");
} else {
elem.setAttribute("style","display:inline;");
}
});
</script>
I'm not sure I understand exactly what you want to do, but your code can be simplified a bit by taking advantage of the shortcuts that jQuery provides.
//When the document is ready...
$(function(){
//Select the arrow just once
var arrow = $("#arrow");
//Attach a scroll event to the window
$(window).scroll(function(){
//See what the scroll position is
var scrollPos = document.body.scrollTop;
//When the document has scrolled to a certain point or more, hide the arrow.
//Otherwise, show it.
if(scrollPos >= 800){
arrow.hide();
} else {
arrow.show();
}
});
});
Here's a brief demo of it in action: http://jsfiddle.net/Bt35Q/
I am kind of really stuck with this problem. Any help will great.
I am clicking on a link which expand the content and when i am cliking on a hide button, instead of taking me to the Expand link, it takes me to the bottom.I have already tried such options like onclick="fun() return false" and href=javascrpit:void(0), but not could help.
PLease refer http://jsfiddle.net/BdsyJ/ this link and click on "How do I maximize battery life" and at the bottom you will get a hide button which should take the control back to the Click it rather than placing the page at the bottom.
Thank you guys.
I changed your ReverseDisplay() method to this and it works nicely:
function ReverseDisplay(d) {
$("#" + d).toggle();
$('html, body').animate({
scrollTop: $("#" + d).prev().offset().top
}, 100);
}
here's a working example:
http://jsfiddle.net/hunter/BdsyJ/5/
In case you were wondering; YES your HTML is invalid. <li> elements should not have <div> siblings.
You're at the bottom of the page because you have hidden so much content. Two things I would update in your code:
cache the element look up so you only do it once and and
scroll the page to the top after you close it using scrollTo(0,0) or
something more complex if you need to scroll back to the exact
element you toggled.
Code:
function ReverseDisplay(d) {
var el = document.getElementById(d);
el.style.display = (el.style.display == "none")?"block":"none";
}