I have the following divs
<div id="item-1" >item 1 content get here </div>
<div id="item-2" >item 2 content get here </div>
I would like to execute a function when the scroll is between the div content and when the scroll goes beyond the div then execute a different function.
In my jQuery I have
$(window).on('scroll', function() {
var divs = ["item-1","item-2"]; //the above two divs
divs.forEach(function(item){
var current_div = $('#'+item).offset().top
if(current_div < window.pageYOffset) {
console.log("I have reached the div", item);
}
});
});
The above works when scrolling to the bottom, but doesn't work when scrolling to top. It also doesn't detect when I scroll beyond a certain div.
How can I detect when scroll is only with a certain div? The divs are dynamic, hence the need to use the array.
Your solution should work with the code below.
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
$(window).on('scroll', function() {
var divs = ["item-1","item-2"]; //the above two divs
divs.forEach(function(item){
var current_div = $('#'+item).offset().top
if(current_div < window.pageYOffset) {
console.log("I have reached the div", item);
}
});
});
And then your HTML
<div id="item-1" >item 1 content get here </div>
<div id="item-2" >item 2 content get here </div>
Note: The only change made is the JQuery inclusion.
Enjoy!
Related
I have the following HTML:
<div class="page">
<div class="somecontent_1">...</div>
<div class="somecontent_2">...</div>
<div class="somecontent_3">...</div>
<div class="somecontent_4">...</div>
</div>
Now I'd like to separate the content with a separate page so it looks something like this:
<div class="page">
<div class="somecontent">...</div>
<div class="somecontent">...</div>
</div>
<div class="newpage">
<div class="somecontent">...</div>
<div class="somecontent">...</div>
</div>
The function checks the height of each class somecontent and if it's larger than a certain amount, I need to move the content to a new page.
My guess is that I would need to create an empty div (newpage) and then fetch the elements after the height is exceeded and move them to the empty newpage and continue iterate like that.
My question would be how I would get all content that are after the last element that reached the height so I can move it to the new empty page that I would create. Other solutions are most welcome if there is a better way of doing it!
The code I came up with looks like this:
var page = $('.page');
var pageHeight = 0;
$.each(page.find('.somecontent'), function() {
if (pageHeight > 1000) {
page.next('<div class="newpage"></div>');
/* Somehow get all elements to add to the newly created page */
page.next('.newpage').append(<NEXT_ELEMENTS>);
pageHeight = 0;
}
pageHeight = pageHeight + $(this).height();
});
When you reach the page which answers the height criterion use the .nextAll function to get all the next siblings of it, then use .wrapAll to wrap them with your newpage div.
Here is the corresponding documentation of nextAll and wrapAll, it has everything you need to cover your scenario.
See comments in line below.
// Instead of the $.each() utiility function
// Just loop over each content area that needs
// examination
$('.somecontent').each(function(index, item) {
// Check if the height of the item is greater than the target (20px for this example)
if (parseInt(getComputedStyle(item).height,10) > 20) {
// Make a new div after the div that the item is currently in
// if one doesn't already exist
if($(".newpage").length === 0){
$(item.closest(".page")).after('<div class="newpage"></div>');
}
// Move the item into the new div
$(item.closest(".page")).next('.newpage').append(item);
}
});
//console.log(document.body.innerHTML); // shows resulting HTML
div.page {border:1px solid red; }
div.newpage {border:1px solid green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="page">
<div class="somecontent">This<br>is<br>some<br>content</div>
<div class="somecontent">This is some content</div>
<div class="somecontent">
<ul>
<li>This</li>
<li>is</li>
<li>some</li>
<li>content</li>
</ul>
</div>
<div class="somecontent">Other</div>
</div>
I've got a single page site with two div modules that expand when clicked.
The first is at the top, in the Hero section and with the current code works as desired. The second is much further down the page in another section. Currently this second module closes as soon as the scroll takes place.
What I need to do is get the documents scroll coordinates when the div is clicked. Then once the user scrolls 200px up or down the div closes back up. Regardless of where it (the div) is on the site.
All the questions I found here and elsewhere only refer to setting the scroll tolerance relative to the window position on page load. But for me that's no goon. This site is responsive, and as it changes the initial positions of the div's will / can be unknown. I need someway of dynamically storing the viewports scroll position when the div's are clicked and assigning the 200px tolerance then.
I hope that makes sense. I've been at this for like 12+hrs now. SOS :)
Here's the Fiddle
If you don't want to go over to Fiddle, Here's the requisite code
HTML:
<body>
<section id="hero">
<div>
<div class="module-cta hero-cta">
<a class="module-cta__button"><!-- Fallback location -->
<span class="module-cta__text">PRESS ME</span>
</a>
<div class="module-cta__open">
<div class="module-cta__open-inner">
<div class="hero-cta__contact-points">
<div class="phone">
<div class="hero-cta_contact_logo">
<span><!-- phone.svg" --></span>
</div><!-- .service-logo -->
<div class="contact_trigger">
<a>Scroll down to 200px to see</a>
</div><!-- .contact_trigger -->
</div><!-- .phone -->
<div class="email">
<div class="hero-cta_contact_logo">
<span><!-- email.svg --></span>
</div><!-- .service-logo -->
<div class="contact_trigger">
<a>this div fold back up</a>
</div><!-- .contact_trigger -->
</div><!-- .email -->
</div><!-- .hero-cta__contact-points -->
<button class="module-cta__close module-cta__cancel"><i class="icon"><span></span></i></button>
</div><!-- .hero-cta__open-inner -->
</div><!-- .hero-cta__open -->
</div><!-- .hero-cta -->
</div>
</section>
<section class="spacer"></section>
<section id="service_area">
<div class="area_input">
<div class="module-cta area-cta wow fadeInUp" id="form_module">
<a class="module-cta__button area-cta__button">
<span class="module-cta__text area-cta__text">NOW PRESS ME</span>
</a>
<div class="module-cta__open area-cta__open">
<div class="module-cta__open-inner area-cta__open-inner">
<div class="area-cta__search">
<form class="postcode_form" id="postcode_form" name="postcode_form" action="#">
<input type="number" id="your_postcode" class="your_postcode" name="postcode" placeholder="3???">
<button type="button" class="area-btn"><span></span></button>
<a class="call-now">##########</a>
</form>
</div><!-- .area-cta__search -->
<button class="module-cta__close module-cta__cancel"><i class="icon"><span></span></i></button>
</div><!-- .area-cta__open-inner -->
</div><!-- .area-cta__open -->
</div><!-- .area-cta -->
</div><!-- .area_input -->
</section>
<section class="spacer"></section>
</body>
Script:
I'm sure a lot of this can be cleaned up and shrunk, but for now I'm just trying to get it all going.
// opens & closes modules by clicking module name
$('.module-cta__button').on('click', function(){
if($(this).parent().hasClass('hero-cta')){
$(this).parent().toggleClass('module-cta--active');
} else {
if($(this).parent().hasClass('area-cta')){
$(this).parent().toggleClass('module-cta--active');
}
}
});
// closes modules with .module-cta__close btn
$('.module-cta__close').on('click', function(){
if($(this).closest('div .module-cta').hasClass('module-cta--active')){
$(this).closest('div .module-cta').removeClass('module-cta--active');
}
});
// closes modules on scroll.
// * works but doesn't apply scroll tolerance of 200px for #area
$(window).scroll(function(){
var currentPos = $(window).scrollTop();
var module = $('div .module-cta');
if(module.hasClass('module-cta--active') && module.position().top <= currentPos+200){
$('div .module-cta--active').removeClass('module-cta--active');
}
});
// closes modules when escape key is pressed
$(window).keydown(function(escape){
var key = escape.which;
if(key == 27){
$('div .module-cta--active').removeClass('module-cta--active');
}
});
see Fiddle for css
Thanks for any help or useful suggestions in advanced.
I've put together a much smaller and simpler demonstration, just to show you what variables you need to accomplish this. Essentially, when a div is clicked, capture the current document scroll position using $(document).scrollTop(). Also store a refference to the current div that has been clicked.
When scrolling, check the difference between the current scroll and the new scroll, and using your clicked div reference, shrink the div when the difference is 200 or greater. JS fiddle below;
https://jsfiddle.net/jLqu4pas/
Code from Fiddle;
var currentScroll;
var lastClickedDiv;
$('section').click(function(){
$(this).css({'height' : '400'})
currentScroll = $(document).scrollTop();
lastClickedDiv = $(this);
console.log(currentScroll);
})
$(window).scroll(function(){
if($(document).scrollTop() > currentScroll + 200){
lastClickedDiv.css({'height' : 0})
}
})
So I've put together a script that will potentially help you.
I've done some basic testing on it, but if you come across any problems make a comment.
// Generate offsets and return them as an object
function generateOffsets($element, tolerance)
{
var offsets = $element.offset(),
offsetTop = offsets.top;
return {
scrollPos: offsetTop,
toleranceTop: offsetTop - tolerance,
toleranceBottom: offsetTop + tolerance
};
}
// Run a callback when the user leaves the scroll tolerance of a set of elements
function closeOnScroll($elements, tolerance, callback)
{
$elements.each(function() {
var $element = $(this),
offsets = generateOffsets($element, tolerance),
resizeEvent;
// On resize, regenerate the offsets so they stay up to date
$(window).on('resize', function(e) {
resizeEvent = e;
offsets = generateOffsets($element, tolerance);
});
// On scroll check if we've left the tolerance area, if so run the event and unbind
$(window).on('scroll', function(e) {
var windowPos = $(this).scrollTop();
if (windowPos < offsets.toleranceTop || windowPos > offsets.toleranceBottom) {
callback($element);
$(this).unbind(e);
$(this).unbind(resizeEvent);
}
});
});
}
// Example: Apply the event to a set of elements
$('.btn').click(function() {
closeOnScroll($('div .module-cta'), 200, function($element) {
$element.removeClass('module-cta--active');
});
});
The thing to remember with thie script is that it needs to be applied EVERY time the user clicks your button. You may say, WHY WOULD YOU DO THAT - but it actually has some serious performance implications.
The events rely on both scroll and resize, both of which are very slow, especially if not debounced like in this script. However, what I do in the script is unbind the events once it has occured. Otherwise, the resize and scroll would keep happening for each and every one of your buttons until the end of time. By 'unbinding' the event, you ensure long running performance of your page.
Wouldn't want to ruin those pretty animations would we?
I have a simple script that toggles the visability of two divs:
<script type='text/javascript'>
$(window).load(function(){
function toggle_contents() {
$('#page1').toggle();
$('#page2').toggle();
setTimeout(function(){
toggle_contents()
}, 25000)
}
toggle_contents();
});
</script>
<div id="container">
<div id="page1">This is page 1 contents.</div>
<div id="page2" style="display:none;">This is page 2 contents.</div>
</div>
It works great but I can not figure out how to add more divs to the mix.
http://jsfiddle.net/mxwv85px/1/
Any help is much appreciated...
To cycle through a set of divs you could use a class on the active div, and use next to move on each iteration. Something like this:
function toggle_contents() {
var $active = $('#container .active');
if ($active.length && $active.next().length) {
$active.hide().removeClass('active').next().show().addClass('active');
}
else {
$('.active').hide();
$('#container div:first').show().addClass('active');
}
setTimeout(toggle_contents, 3000)
}
toggle_contents();
Updated fiddle
.toggle() means the div's are toggled between hidden and displayed. I would suggest using .hide() and .show() instead, as this gives you more control about what content you want to display or not. However, the downside is you would need a code that has much more lines to it. Give me a second while I try to make such a thing for you.
Currently you can only have 2 divs, because the .toggle() function can only have 2 values, which means a third div will have the same value as another div, causing it to be either visible or hidden while another div is as well.
The code provided in this answer by #Rory McCrossan is already working, so I'll stop trying to program it myself:
https://stackoverflow.com/a/27447139/4274852
You could cycle through the selected elements and show only one each call
var page=0;
function toggle_contents() {
$('.page').hide();
var array = $('.page').toArray();
$(array[page]).show();
page=++page%array.length;
setTimeout(function(){toggle_contents()}, 3000)
}
toggle_contents();
http://jsfiddle.net/mxwv85px/9/
First of all, put timer out of toggle_contents function. Secondly, add to divs common class, cache them and operate with variable-cache
$(window).load(function(){
var divs = $('.some-class');
function toggle_contents() {
divs.toggle();
}
setTimeout(function(){
toggle_contents()
}, 25000)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="some-class">
</div>
<div class="some-class">
</div>
<div class="some-class">
</div>
You can do this
http://jsfiddle.net/mxwv85px/13/
The code
<div id="container">
<div id="page1">This is page 1 contents.</div>
<div id="page2" style="display:none;">This is page 2 contents.</div>
<div id="page3" style="display:none;">This is page 3 contents.</div>
<div id="page4" style="display:none;">This is page 4 contents.</div>
<div id="page5" style="display:none;">This is page 5 contents.</div>
function toggle_contents() {
var items = $('#container div');
for(var i= 0; i < items.length; i++)
{
if($(items[i]).is(":visible")) {
$(items[i]).hide();
i + 1 == items.length ? $(items[0]).show() : $(items[i+1]).show();
break;
}
}
setTimeout(function(){ toggle_contents() }, 500)
}
toggle_contents();
To add more divs, you can use .append, for example:
$('#container').append('<div id="page3">This is page 3 contents</div>');
Okay, long story short, what I am trying to do is change the jquery around to have the new section populate slide down on top of the old div, and then slide up the old div, to make only the new content box appear. as of right now, the code works perfectly sliding up the old section, and then sliding down the new content.
Code Sample for The Javascript
$(document).ready(function(){
function doTab() {
$(".active").removeClass("active");
$(this).addClass("active");
$(".content").slideUp();
var content_show = $(this).attr("title");
$("#"+content_show).slideDown();
}
// When a link is clicked
$("a.tab").click(doTab);
if (window.location.hash) {
$('a[title="' + window.location.hash.substring(2) + '"]').click();
}
});
Code for the tabs and sections
<ul id="tabs">
<li>type1</li>
<li>type2</li>
</ul>
<!--<div class="dpoint-holder"><div class="dpoint"></div></div>-->
<section id="type1" class="content">
<p>contents1contents1contents1contents1contents1</p>
</section>
<section id="type2" class="content content_2">
<p>contents2contents2contents2contents2</p>
</section>
$(document).ready(function() {
$('div.post').click(function() {
// the clicked LI
var clicked = $(this);
// all the LIs above the clicked one
var previousAll = clicked.prevAll();
// only proceed if it's not already on top (no previous siblings)
if(previousAll.length > 0) {
// top LI
var top = $(previousAll[previousAll.length - 1]);
// immediately previous LI
var previous = $(previousAll[0]);
// how far up do we need to move the clicked LI?
var moveUp = clicked.attr('offsetTop') - top.attr('offsetTop');
// how far down do we need to move the previous siblings?
var moveDown = (clicked.offset().top + clicked.outerHeight()) - (previous.offset().top + previous.outerHeight());
// let's move stuff
clicked.css('position', 'relative');
previousAll.css('position', 'relative');
clicked.animate({'top': -moveUp});
previousAll.animate({'top': moveDown}, {complete: function() {
// rearrange the DOM and restore positioning when we're done moving
clicked.parent().prepend(clicked);
clicked.css({'position': 'static', 'top': 0});
previousAll.css({'position': 'static', 'top': 0});
}});
}
});
});
How can I move a div to the top of a list of divs upon clicking a link.
eg;
<div id=1>Div One <a>Click to update</a><a>a different link</a></div>
<div id=2>Div One <a>Click to update</a><a>a different link</a></div>
<div id=3>Div One <a>Click to update</a><a>a different link</a></div>
<div id=4>Div One <a>Click to update</a><a>a different link</a></div>
<div id=5>Div One <a>Click to update</a><a>a different link</a></div>
and when you click ONLY on the link "CLICK TO UPDATE" for any div, it should move that div to the top of the page!
80% done. Thanx guys for the swift response. Preciate it to the max. Anyways thanx to #valipour I managed to get the div to move ONLY when you click on a specific link by adding a class to the link and changing my first two lines from;
$('div.post').click(function() {
// the clicked LI
var clicked = $(this);
to;
$("a.rep").click(function() {
var clicked = $(this).closest("div.post");
html code is;
<div id="wrapper">
<div class="post"><a class="rep">1 Aasdjfa</a> <a>6 Aasdjfa</a></div>
<div class="post"><a class="rep">2 Aasdjfa</a> <a>7 Aasdjfa</a></div>
<div class="post"><a class="rep">3 Aasdjfa</a> <a>8 Aasdjfa</a></div>
<div class="post"><a class="rep">4 Aasdjfa</a> <a>9 Aasdjfa</a></div>
<div class="post"><a class="rep">5 Aasdjfa</a> <a>10 Aasdjfa</a></div>
</div>
Thanks!
BUT it doesn't work with div's that were dynamically loaded? eg. I have 10 divs showing by default then I have a script that loads more divs when you scroll...this script won't move the divs in the autoload section when you click on any of them...any idea why???
If you put them all inside another wrapper div you could do
(This is now the most upto date version):
$("#wrapper a.rep").live('click', function(){
$(this).parents('.post').hide().prependTo("#wrapper").slideDown();
});
EDITED my answer. This one works. With animation aswell ;).
If you dont like animation, have just $(this).parent().prependTo("#wrapper") **
http://jsfiddle.net/2DjXW/18/
To load Divs that are dynamically added afterwards, you will have to use the 'live'. When document is ready the divs that are not there cannot have events added. But live adds the events when new are dynamically added.
New edit:
http://jsfiddle.net/tVhqz/8/
Should work now well.
give "update" class to those links that you want to do the action and then:
$("a.update").click(function()
{
var myparent = $(this).closest("div");
var parentparent = myparent.parent();
myparent.detach().prependTo(parentparent );
return false;
});
jsFiddle link: http://jsfiddle.net/g56ap/4/
NOTES:
we are keeping parentparent separately because myparent.parent() would be invalid after detach.
Ok so through a combination of #valipour and #pehmolenu scripts I was able to get what I was looking for. I have tested it only on chrome but am sure it should work on other browsers. The complete working code is below.
Javascript;
$("#wrapper a.rep").live('click', function(){
$(this).parents('.post').hide().prependTo("#wrapper").slideDown();
});
html;
<div id="wrapper">
<div class="post"><a class="rep">This will Move div</a> <a>This won't</a></div>
<div class="post"><a class="rep">This will Move div</a> <a>This won't</a></div>
<div class="post"><a class="rep">This will Move div</a> <a>This won't</a></div>
<div class="post"><a class="rep">This will Move div</a> <a>This won't</a></div>
<div class="post"><a class="rep">This will Move div</a> <a>This won't</a></div>
</div>
This will also work if you divs are loaded dynamically based on scroll!
Thanx guys! See it in action at repjesus.com! click "rep it" on any item!