Scrollable Div with Sticky header - javascript

I am trying to make something where I have a div that scrolls. Inside the div there are "title" elements and as I scroll I want the title element of that section to stick to the top of the div and remain there like a header.
Sort of like what people see on webpages where the menu sticks to the stop of a page as you scroll. This example can clearly be seen on the Mac OS X calendar in the "Day" view.
I think I can deal with the making the element stick part, I saw an interesting solution that I think I can adapt. However I was wondering if someone can help me figure out how to know if a title element has reached the top of a scrollable div.
The use case is as follows:
<div class="container">
<div class="floatLeft">
<div class="scrollingDiv" style="height:100px; overflow-y:scroll; overflow-x:hidden;">
<ul>
<li>
<div>
<h4>Title</h4>
<div>Content</div>
</div>
</li>
<li>
<div>
<h4>Title</h4>
<div>Content</div>
</div>
</li>
</ul>
</div>
</div>
<div class="floatRight"></div>
</div>
How would I know when the second "Title" has hit the top of my "scrollingDiv", not the top of the page itself?
Thanks!

You can make an element sticky by setting it's top property to zero and making setting position:absolute; via css.
.stickyTop {
position:absolute;
top:0px;
}
This will ensure the element stays at the top of the page always.
To identify if an element has reached the top of window you can use offset() to identify the current position and check if it's neared the top.
$('div.scrollingDiv').scroll(function() {
var active = null;
$('.scrollingDiv h4').each(function(idx, val) {
var topOffset = $(val).offset().top;
if (topOffset < 20) // elem is 20 px from top
{
// Element nearest the top
active = $(val);
}
console.log(active); // Element closest to the top
});
});
The above simply loops though all the h4 properties searching for the element closest to the top of the page, and logs it.
You can combine these together to create something like a Funky jsFiddle.

Related

Scroll element inside div that's scrollable into view

I have a little bit of a problem I'm a beginner programmer and writing my first web app, I came across a little bit of a problem.
I have a parent div with a height of 300px and inside that div I have 20 child divs and every single one of them has different id attribute and one div gets a special class that changes his appearance. I want that div always be seen by user so they don't have to scroll to find it.
I tried:
const renderStandings = (team,teamID) =>{
let cssClass = "team-standing";
if(team.team.id === teamID){
cssClass = "team-standing favouriteTeam";
}
HTML markup
const markup = `
<div class="${cssClass}" data-teamID="${team.team.id}">
<div class="team-position"><p>${team.position}</p></div>
<div class="team-crest"><img src="src/img/logos/${team.team.id}.svg"></div>
<div class="team-name"><p>${team.team.name}</p></div>
<div class="team-gamesplayed"><p>${team.playedGames}</p></div>
<div class="team-goaldiffrence"><p>${team.goalDifference}</p></div>
<div class="team-points"><p>${team.points}</p></div>
</div>
`;
document.querySelector('.league-standings').insertAdjacentHTML('beforeend',markup);
Scroll into view
if(document.querySelector('.favouriteTeam')){
var el = document.querySelector('.favouriteTeam');
el.scrollIntoView({
behavior: 'auto',
block: 'center',
inline: 'center'
});
}
}
}
That scrolls that div into the view exactly how I wanted it to, but it also scrolls the whole website to the parent div and I don't want that.
QUESTION
So the question is how do I scroll that div into view without scrolling the whole website. Preferably I would love to have answer in vanilla JS because that's what I'm practicing at the moment, and I don't plan to learn any frameworks until I'm comfortable with JS.
Illustrated result of what I want to achieve:
This is now and it scrolls the whole website
This is how I want it to be
[2]: https://i.stack.imgur.com/Ldg3N.jpg
Inside my problem the answer was to use (scrollTop). Since every child div had 50px height I added atribute postition that gave me the position of element that I wanted to center (1-20), thanks to that I could calculate value of scrollTop for the element to be exacly in the center of scrollable div. Since 7 of them is already visible i started at 8th position.
if(document.querySelector('.favouriteTeam').getAttribute('data-teamposition')>7){
document.querySelector('.league-standings').scrollTop = 200 + ( 50 * (document.querySelector('.favouriteTeam').getAttribute('data-teamposition') - 8 ));
}

Why does my slimscroll have issues when the height is set at a percentage as opposed to a fixed value?

I am using this slimscroll library to display a better looking scroll bar for some divs on my page. I noticed that if I set the height of my scrollbar class to a fixed value (i.e. '500px', '80vh' etc.) then if the content does not overflow, the scrollbar will not be present and if it does, then it will appear. It works great.
However this solution doesn't work well because I need the div that encompasses the scrollbar to take up 100% of the parent div. However when I do that, the scrollbar will appear even if the content does not overflow the container.
Does anyone know a solution where I can set the height of the scrollbar to be 100% and have it behave so that it only appear when content overflows the parent container?
HTML
<div class="s12 m12 l12 row-90 m-b-0 rail-row">
<div class="card-scroll m-r-10">
<ul class="top-list">
///adds a series of divs based on data from back end
{{#each ins in selectedInstruction}}
<li>{{> Print_job printJobArgs ins true true}}</li>
{{/each}}
</ul>
</div>
</div>
JS
$('.card-scroll').slimScroll({
size: '8px',
height: '100%',
});
$(".slimScrollBar").hide()
When the page gets loaded, slimscroll encompasses the div with the class 'card-scroll' in a div and adds html to make its scrollbar appear.

Getting dynamic css top value from element using jQuery

I am using the jScrollPane jQuery plugin in a project, and the scrollable area has a couple of list item elements, and each list item when is clicked it needs to trigger a modal box positioned absolute from in the center, filling all the space. Anyway its kind of hard to explain, but this is not the problem. The problem is that, I need to get the negative top css value (when it scrolls down) via jQuery and I can't manage to make it work, i tried using the .css() selector but it always returns 0px even if in the source shows different. Any idea ?
EDIT:
<div id="provider-menu">
<ul>
<li class="menu-item"> <h2> content here </h2>
<div class="more-info">
more info box here
</div>
</li>
<!-- More List Items Here -->
</ul>
</div><!-- end #provider-menu -->
<!-- Javascript -->
$("#provider-menu").jScrollPane();
// jspPane is added by the plugin
var topValue = $(".jspPane").css("top"); // returns 0px every time on scroll even if in the source is different.
I am trying to do this because I can't change the HTML structure, the .more-info div has to fill the #provider-menu box which has a predefined width and height, but if I set it to absolute top left right bottom it goes to top and you have to scroll to see what's there. So I was thinking on getting the top negative value, remove the minus and set the top value to the .more-info box.
$(element).css behaves differently than you might expect.
This actually works: $(".jspPane")[0].style.top
What you have demands absolute positioning I believe, which jspPane does not have.
Either way, the above code gives you that actual ( usually negative ) pixel value.

Hide child <div>s as parent div shrinks?

I am trying to write a zoom in/out feature on a web app I am making using the jqueryUI slider.
I am having difficulty handling when my parent div shrinks too much, and cramps its child containers.
<div class="puck originator inline-block" style="width: 310.5px; left: 0px;">
<div class="conflicted inline-block originator">
<div class="right-number">I should stay</div>
<div class="left-number">I should stay</div>
<div class="middle-number">I Should disapper</div>
</div>
</div>
Here is the relevant section of code I have
http://jsfiddle.net/aQKwE/
Basically I have the parent div (class 'puck') that is being shrunk using a jquery slider. For this code I just used a text box, but same idea.
When I shrink that div, the containing divs stick around and are very garbled.
I want to be able to remove the middle child div when it becomes to cramped, leaving the left and right child divs to occupy all the space
Furthermore, if it becomes to cramped yet after that, I want to remove the right div, leaving only the left.
Finally I want to be able to remove all contents so that nothing more than the background of the parent shows.
Is there a way to do this easily, preferably through CSS? I don't want to write more javascript code to set 'display:none' on each child div, since it seems like some CSS rules should handle this.
Any ideas?
There's not really any logic built into CSS to handle something like this. You can set rules based on viewport size, but that won't help in this case.
I updated your jsfiddle with this code so you can test it and see what you think, but essentially I just added some checks in your javascript function to hide based on the width submitted.
var newwidth = $('#text').val();
$(".middle-number").show();
$(".right-number").show();
if (newwidth < 280) {
$(".middle-number").hide();
}
if (newwidth < 180) {
$(".right-number").hide();
}
$('.puck').css('width',newwidth);

Scrolling to the next element

I'm struggling with a jquery or javascript problem.
It already got annoying which tells me I might think too complicated on this one.
So my markup (simplyfied) looks like this:
<div class="container">
My Content
scroll down
</div>
<div class="container">
My Content
scroll down
</div>
<div class="container">
My Content
scroll down
</div>
<div class="container">
My Content
scroll down
</div>
Basically just some containers.
Each one contains different content and a button.
The Plan:
1) After a click on a button the window should scroll down to the next container.
2) The last button scrolls to the first container again. So I need a loop.
3) The numbers of containers may change from page to page.
EDIT: 4) The containers may not always be direct siblings to each other (see markup below)
The Problem:
I could get this to work by giving each container a unique ID as a target for the scroll effect.
The problem with that is that it gets too messy quickly.
Cant I just somehow target "the next object with the class: container", and scroll to that?
I'm not sure if js or jquery is the right approach. My knowledge in both is somewhat limited.
I would be really grateful for a push in the right direction.
EDIT: The containers may not always be direct siblings of each other.
<div class="row">
<div class="container">
My Content
scroll down
</div>
<div class="container">
My Content
scroll down
</div>
</div>
<div class="container">
My Content
scroll down
</div>
<div class="container">
My Content
scroll down
</div>
<div class="row">
<div class="container">
My Content
scroll down
</div>
<div class="container">
My Content
scroll down
</div>
</div>
Simple solution:
To get the next container, try using next().
Basically, the <div> containers are siblings of each other, so calling .next() on one div container will give you the next.
$(".button").on("click", function(e) {
$(document).scrollTop($(this).parent().next().offset().top);
// $(this).parent().next() // this is the next div container.
return false; // prevent anchor
});
http://jsfiddle.net/Pm3cj/1/
You just use $(this) to get the link object, .parent() to get the parent of the link, which is the <div>, then .next() to get the next sibling (note it will wrap automatically, so the sibling after the last <div> is the first <div>!),.offset()to get its position relative to the page,.top` to get it relative to the top border.
Then you just use $(document).scrollTop() to scroll to that location.
For a completely general solution, use:
$(".button").on("click", function(e) {
container = $(this).parent();
// if I am the last .container in my group...
while ( document != container[0] // not reached root
&& container.find('~.container, ~:has(.container)').length == 0)
container = container.parent(); // search siblings of parent instead
nextdiv = container.nextAll('.container, :has(.container)').first();
// no next .container found, go back to first container
if (nextdiv.length==0) nextdiv = $(document).find('.container:first');
$(document).scrollTop(nextdiv.offset().top);
// $(this).parent().next() // this is the next div container.
return false;
});
​The code basically uses container.find('~.container, ~:has(.container)') to find any sibling that has or is a .container. If nothing, then go up the DOM tree 1 step.
After it finds something which is or has a .container, it grabs it with nextdiv = container.nextAll('.container, :has(.container)').first();.
Lastly, if nothing is found, checked by nextdiv.length==0, just grab the first .container in the whole page.
Then scroll to whatever .container was grabbed.
http://jsfiddle.net/Pm3cj/3/
To animate the scroll, place the scrollTop property in an animate function:
// $(document).scrollTop(nextdiv.offset().top); // snaps to new scroll position
$('body').animate({scrollTop:nextdiv.offset().top},300); // animates scrolling
http://jsfiddle.net/Pm3cj/4/
JavaScript is not required for this. You can use HTML anchors.
<div class="container" id="first">
My Content
scroll down
</div>
<div class="container" id="second">
My Content
scroll down
</div>
<div class="container" id="third">
My Content
scroll down
</div>
<div class="container" id="fourth">
My Content
scroll down
</div>
What you want can be easily achieved through parent() and child().
If the number of containers on each page is different, then you should start ID'ing (don't know if that's a term) containers serially. Something like, class="container-1"
The click event on the last button should do something like:
var num = $('div[class^="container-"]').filter(function() {
return((" " + this.className + " ").match(/\scontainer-\d+\s/) != null);
});
num++;
var last_container = $(this).parent('.container' + num);
last_container .scrollTo();
Am sure you can figure out what the next button should do ;)

Categories