Use elementFromPoint combined with scroll position - javascript

I would ask for help for the following problem. Given is a long list of divs with names. Each div with a name has a data-index with the initial letter of that person.
A small div sticked to the scrollbar indicator should name the initial letter from the current person next to it (Indicated by the blue line). Further the Text inside the div should be styled red otherwise black and the header of 100px should be respected
This is what I have:
window.onscroll = function(event){
// Variables for scroll pos calculation
var viewPortHeight = window.innerHeight;
var documentHeight = document.documentElement.scrollHeight;
var scrolledAmount = document.documentElement.scrollTop;
var indicatorTop = scrolledAmount * viewPortHeight / documentHeight;
// Highlight current active name and get data attr
// THIS PART IS NOT WORKING
var el = document.elementFromPoint(0, scrolledAmount);
el.style.color = 'red';
var el_text = el.dataset.index;
// Stick the index div to the scrollbar
if (indicatorTop > 110) {
document.getElementById('index').style.marginTop = indicatorTop-110+'px';
document.getElementById('line').style.marginTop = indicatorTop-110+'px';
document.getElementById("index").innerHTML = el_text;
} else {
document.getElementById('index').style.marginTop = '0px';
document.getElementById('line').style.marginTop = '0px';
}
}
body, html {
margin: 0;
}
.person__wrapper {
margin-top: 120px;
}
.person__item {
padding: 20px;
font-size: 20px;
background: #efefef;
margin: 20px;
width: 90%
}
#index, #line{
position: fixed;
top: 110px;
right: 0;
padding: 10px;
background: #0000ff;
color: #fff;
font-family: ClarendonBT-Heavy
}
#line {
width: 100%;
height: 1px;
padding: 0;
margin:0
}
.header__wrapper {
position: fixed;
top: 0;
width: 100%;
height: 100px;
background: white;
border-bottom: 2px solid #000;
opacity: 0.3;
}
<header class="header__wrapper"></header>
<section class="person__wrapper">
<article data-index="A" class="person__item">Adam</article>
<article data-index="B" class="person__item">Bobby</article>
<article data-index="C" class="person__item">Carla</article>
<article data-index="D" class="person__item">Debby</article>
<article data-index="E" class="person__item">Emil</article>
<article data-index="F" class="person__item">F</article>
<article data-index="G" class="person__item">G</article>
<article data-index="H" class="person__item">H</article>
<article data-index="I" class="person__item">I</article>
<article data-index="J" class="person__item">J</article>
<article data-index="K" class="person__item">K</article>
<article data-index="L" class="person__item">L</article>
<article data-index="M" class="person__item">M...</article>
<article data-index="N" class="person__item">N...</article>
<article data-index="O" class="person__item">O...</article>
<article data-index="P" class="person__item">P...</article>
<article data-index="Q" class="person__item">Q...</article>
<article data-index="R" class="person__item">R...</article>
<article data-index="S" class="person__item">S...</article>
<article data-index="T" class="person__item">T...</article>
<article data-index="U" class="person__item">U...</article>
<article data-index="V" class="person__item">V...</article>
<article data-index="W" class="person__item">W...</article>
<article data-index="X" class="person__item">X...</article>
<article data-index="Y" class="person__item">Y...</article>
<article data-index="Z" class="person__item">Z...</article>
</section>
<div id="index">A</div>
<div id="line"></div>
Here is a working fiddle: https://codepen.io/t-book/pen/zYvZzxV
All is working fine except elementFromPoint is not targeting the element which has the y amount of my scrolled height but all elements (all get red). Further, the data attribute is not updated inside by index div.
Can one push me in the right direction?

I found your question while searching a problem of my own. Little stale, but if you're still looking for a solution maybe this will help. There were a few issues going on:
elementFromPoint uses coordinates relative to the viewport, not the document. So using a y value of scrolledAmount probably wasn't doing what you really wanted. 0 would be the top of the viewport. More on this in a bit.
but...since the header is fixed, it always gets returned. Two ways to solve this. The easiest is to add css pointer-events:none on the header. Then it gets ignored. But if you need pointer events, the alternative would be to use elementsFromPoint which will return an array, and you can skip the header and grab the next element in the array.
since you've got a margin on your items, when the margin passes over the point you're querying it "falls through" to the underlying section tag. Unfortunately the best way around this seems to be a short for loop that can move the point slightly until you find the next article tag, and break the loop as soon as a match is found. (I wish there was a better way...that's what I was looking for when I found your question!)
I'm also using an x coord of window.innerWidth/2 for the same reason, to get past left margins and such. Of course this could be tweaked to meet your needs.
the reason why all of the headers were turning red is because fairly quickly after you started scrolling, your elementFromPoint was falling through and hitting a higher level element. And since color is an inherited property, it was being applied on all the articles as children. This can be prevented by the .closest('article') thing I added. That way if it's not an article or a descendant of an article, it will be null...not the wrong element.
window.onscroll = function(event) {
// Variables for scroll pos calculation
var viewPortHeight = window.innerHeight;
var documentHeight = document.documentElement.scrollHeight;
var scrolledAmount = document.documentElement.scrollTop;
var indicatorTop = scrolledAmount * viewPortHeight / documentHeight;
// Highlight current active name and get data attr
for (var i = 0; i < 80; i += 10) {
var el = document.elementFromPoint(window.innerWidth / 2, i).closest('article');
if (el) break;
}
if (el) {
el.style.color = 'red';
var el_text = el.dataset.index;
}
document.getElementById("index").innerHTML = el_text || 'A';
// Stick the index div to the scrollbar
if (indicatorTop > 110) {
document.getElementById('index').style.marginTop = indicatorTop - 110 + 'px';
document.getElementById('line').style.marginTop = indicatorTop - 110 + 'px';
} else {
document.getElementById('index').style.marginTop = '0px';
document.getElementById('line').style.marginTop = '0px';
}
}
body,
html {
margin: 0;
}
.person__wrapper {
margin-top: 120px;
}
.person__item {
padding: 20px;
font-size: 20px;
background: #efefef;
margin: 20px;
width: 90%;
box-sizing: content-box;
}
#index,
#line {
position: fixed;
top: 110px;
right: 0;
padding: 10px;
background: #0000ff;
color: #fff;
font-family: ClarendonBT-Heavy
}
#line {
width: 100%;
height: 1px;
padding: 0;
margin: 0
}
.header__wrapper {
position: fixed;
top: 0;
width: 100%;
height: 100px;
background: white;
border-bottom: 2px solid #000;
opacity: 0.3;
}
<header class="header__wrapper" style="pointer-events:none"></header>
<section class="person__wrapper">
<article data-index="A" class="person__item">Adam</article>
<article data-index="B" class="person__item">Bobby</article>
<article data-index="C" class="person__item">Carla</article>
<article data-index="D" class="person__item">Debby</article>
<article data-index="E" class="person__item">Emil</article>
<article data-index="F" class="person__item">F</article>
<article data-index="G" class="person__item">G</article>
<article data-index="H" class="person__item">H</article>
<article data-index="I" class="person__item">I</article>
<article data-index="J" class="person__item">J</article>
<article data-index="K" class="person__item">K</article>
<article data-index="L" class="person__item">L</article>
<article data-index="M" class="person__item">M...</article>
<article data-index="N" class="person__item">N...</article>
<article data-index="O" class="person__item">O...</article>
<article data-index="P" class="person__item">P...</article>
<article data-index="Q" class="person__item">Q...</article>
<article data-index="R" class="person__item">R...</article>
<article data-index="S" class="person__item">S...</article>
<article data-index="T" class="person__item">T...</article>
<article data-index="U" class="person__item">U...</article>
<article data-index="V" class="person__item">V...</article>
<article data-index="W" class="person__item">W...</article>
<article data-index="X" class="person__item">X...</article>
<article data-index="Y" class="person__item">Y...</article>
<article data-index="Z" class="person__item">Z...</article>
</section>
<div id="index">A</div>
<div id="line"></div>
*edit: I ended up finding another way to get an element near or under the cursor, in spite of the "margin" problem. But it's not any simpler and I'm not sure if it's really better. It uses caretRangeFromPoint.
var el;
var rng = document.caretRangeFromPoint(window.innerWidth / 2, 0);
//console.log(rng);
if (rng) {
el = rng.startContainer;
if (!(el instanceof Element)) el = el.parentElement;
el = el.closest('article');
}

Related

How to create a horizontal, circular/scrollable menu?

How can we make a horizontal row of link elements (with variable width/text length) with overflow hidden (or without, depending on how this is usually done..) function so that the last element is positioned behind the first and so on in each left or right direction, to create a circular scroll?
I have this so far:
const horizontalContainer = document.querySelector('.horizontal-container')
const horizontalLinks = document.querySelectorAll('.horizontal-link')
let touchStart = 0
let touchX = 0
let isDragging = false
const handleTouchStart = (e) => {
touchStart = e.clientX || e.touches[0].clientX
isDragging = true
}
const handleTouchMove = (e) => {
if (!isDragging) return
touchX = e.clientX || e.touches[0].clientX
touchStart = touchX
horizontalLinks.forEach(element => {
element.style.transform = "translate(" + (touchStart) + "px," + "0px)";
})
}
const handleTouchEnd = () => {
isDragging = false
}
horizontalContainer.addEventListener('mousedown', handleTouchStart)
horizontalContainer.addEventListener('mousemove', handleTouchMove)
horizontalContainer.addEventListener('mouseleave', handleTouchEnd)
horizontalContainer.addEventListener('mouseup', handleTouchEnd)
horizontalContainer.addEventListener('selectstart', () => { return false })
.horizontal-container {
display: flex;
overflow-x: hidden;
width: 100%;
}
.horizontal-container::-webkit-scrollbar {
display: none;
}
.horizontal-link {
padding-left: 20px;
padding-right: 20px;
right: 0;
}
<div class="horizontal-container">
<div class="horizontal-link">
ONE
</div>
<div class="horizontal-link">
TWO
</div>
<div class="horizontal-link">
THREE
</div>
<div class="horizontal-link">
FOUR
</div>
<div class="horizontal-link">
FIVE
</div>
</div>
Edit: Unless you have the time to show me an example, I'm more than happy with just an explanation for how this can be done calculating translate: transform(x,y) to reposition the links when the left or right position of a link div of variable width reaches the right or left position of the screen depending on the screen width, which can also be variable, so that what the exact amount of overflow that peeks outside the viewport on the right will peek out the same amount on the left side of the viewport.
Edit2: Even though I know little about programming or the Javascript language (yet) I do know that this is not a "carousel" which is much easier to implement, that I already have created on my own so I know every detail of it. And a scrollbar is also programmed to move between a left end or right end position - this cannot be used here without a lot of ugly hacks so a new scrolling function needs to be implemented from scratch. I also know that jQuery will not help me to understand or learn more, and that this is nothing one would use - ever - whether you are an amateur or not.
What you are requesting is a carousel pattern. You can configure a carousel to show multiple slides at once. In this case each "slide" would be a menu item.
I have mocked up an example using https://kenwheeler.github.io/slick/
The only downfall is that it snaps to each slide, which may or may not be what you want. If it is not what you want then you would be best looking at other slider alternatives.
But the main point is that what you are requesting is normally done with a slider/carousel pattern. You just need to look at it differently, and you are not limited to show one "slide" at a time.
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel#1.8.1/slick/slick.css"/>
<div class="menu-slider" style="width: 400px;">
<div style="padding:20px;">Link 1</div>
<div style="padding:20px;">Another Link 2</div>
<div style="padding:20px;">Yet Another Link 3</div>
<div style="padding:20px;">Menu Link 4</div>
<div style="padding:20px;">Link 5</div>
<div style="padding:20px;">Menu Link 6</div>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/slick-carousel#1.8.1/slick/slick.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$('.menu-slider').slick({
dots: false,
infinite: true,
centerMode: false,
variableWidth: true
});
});
</script>
Here's a working solution for one that will "fill" its parent in order to create the effect.
I was working on an alternative that didn't fill but it still needs more work.
$(function () {
init();
});
let base_width = 0;
function init() {
setupMenu();
}
function handleScroll(event) {
if (base_width === 0) {
// no need to do anything
return;
}
const $menu = $(event.currentTarget);
const scroll_left = $menu[0].scrollLeft;
// check backwards scroll
if (scroll_left <= base_width) {
const new_left = 2 * base_width - (base_width - scroll_left);
$menu[0].scrollLeft = new_left;
return;
}
if (scroll_left < base_width * 2) {
return;
}
// get remainder
const new_left = scroll_left % (base_width * 2);
$menu[0].scrollLeft = new_left + base_width;
}
function setupMenu() {
const $menu = $("#menu-fill");
const $parent = $menu.parent();
const menu_width = $menu.width();
const parent_width = $parent.width();
if (menu_width >= parent_width) {
// no need to duplicate
return;
}
base_width = menu_width;
// setup a base to clone
const $template = $menu.clone();
// get num times to duplicate to "fill" menu (i.e. allow scrolling)
// NOTE: we duplicate 1 "extra" so that we can scroll "backwards"
const num_duplicate = Math.ceil(parent_width / menu_width) + 2;
for (let i = 0; i < num_duplicate; i++) {
const $new_menu = $template.clone();
$menu.append($new_menu.children());
$new_menu.remove();
}
$menu[0].scrollLeft = base_width;
$menu.scroll(handleScroll)
}
* {
box-sizing: border-box;
font-family: sans-serif;
}
*::-webkit-scrollbar {
background-color: transparent;
height: 6px;
width: 6px;
border-radius: 3px;
}
*::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 3px;
}
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.wrap {
width: 250px;
height: 100px;
border: 2px solid #777;
margin: 0 auto 20px auto;
}
.wrap.fill {
width: 500px;
}
.menu {
display: inline-flex;
max-width: 100%;
overflow: auto;
}
.item {
padding: 5px 10px;
background-color: #426ac0;
color: #fff;
border-right: 1px solid #1d3464;
white-space: nowrap;
}
.item:last-child {
border-right: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrap fill">
<div class="menu" id="menu-fill">
<div class="item">Item 1</div>
<div class="item">Number 2</div>
</div>
</div>

How to set the window size on a div as oppose to the entire window? [jQuery]

Good day,
I've came across the following CodePen from Estelle Pasquin. I would like to add this Apple-like scroll effect to my website.
Getting this to work on my site is no problem. However I would like to place content above this animation and this is where the problem comes in.
Based on the above CodePen content is structured like this:
<div class="intro mac"></div>
<div id="mac" class="mac"></div>
<div class="content">
<h2>Heading</h2>
<p>Content here</p>
</div>
However I need additional content above this animation section, like:
<div class="contentOne">
<h2>Heading 1</h2>
<p>Content here</p>
</div>
<div class="animationContent">
<div class="intro mac"></div>
<div id="mac" class="mac"></div>
</div>
<div class="content">
<h2>Heading 2 </h2>
<p>More content here</p>
</div>
In other words:
User scrolls through website content
User gets to a content break section where they see this big image, scroll, see the nice animation
User continues scrolling through rest of site
However this existing code only works when placed at the top of the page. Like a hero image.
In the JS file is can see the following variable declarations:
var $window = $(window);
var $intro = $('.intro');
var $mac = $('#mac');
var $h1 = $('h1');
My attempt
Looking at
var $window = $(window);
I can see the scroll is set on the window size. However I was thinking can't this be changed to a div. For example
var $window = $('#animationContent');
I would like this animation to work exactly as is just within a div placed on the page as opposed to being the first piece of content.
$(function() {
// cache all elements before we do anything
var $window = $(window);
var $intro = $('.intro');
var $mac = $('#mac');
var $h1 = $('h1');
// define variables and prefill
var offset = $mac.offset();
var windowHeight = $window.height();
var windowTop = $window.scrollTop();
var scrollPercent = (offset.top - windowTop) / offset.top;
var scale = 'scale(' + scrollPercent + ')';
// listen to scroll
$window.on('scroll', function() {
windowTop = $window.scrollTop();
// hide intro if point is reached
if (windowTop >= 940) {
$intro.hide();
} else {
if (windowTop < 200 && windowTop > 100) {
// only perform in a 100px range
$h1.fadeOut(500);
}
$intro.show();
scrollPercent = (offset.top - windowTop) / offset.top;
scale = 'scale(' + scrollPercent + ')';
$intro.css({
'transform': scale
});
}
});
});
#import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,800);
body {
color: #333;
font-family: Open sans;
}
h1 {
position: fixed;
top: 50%;
transform: translateY(-50%);
width: 100%;
text-align: center;
font-size: 52px;
z-index: 999;
color: rgba(255, 255, 255, 0.3);
}
.mac {
height: 613px;
width: 764px;
margin: 1340px auto 100px;
background: white url("https://i.imgur.com/AyELvGR.jpg") no-repeat 0 0;
background-size: 764px 613px;
backface-visibility: hidden;
}
.mac.intro {
position: fixed;
width: 2548px;
height: 2052px;
background-size: 100% auto;
margin: 0;
top: 0;
left: 50%;
margin-top: -300px;
margin-left: -1274px;
transform-origin: 50%;
}
.content {
width: 500px;
margin: 0 auto 150px;
}
.content h2 {
font-size: 52px;
line-height: 1.0865;
font-weight: 300;
margin-bottom: 48px;
}
.content p {
font-size: 18px;
margin: 1em 0 0 0;
line-height: 1.8;
color: #555;
font-weight: 400;
}
.content p a {
color: #555;
text-decoration: underline;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<h1>Just scroll!</h1>
<div class="intro mac"></div>
<div id="mac" class="mac"></div>
<div class="content">
<h2>Apple iMac scrolling effect.</h2>
<p>
iMac 27" mockup by Koy Carraway.<br/> Picture from WallWideHD - Transfagarasan.<br/> Read more about Transfagarasan <a href="http://en.wikipedia.org/wiki/Transf%C4%83g%C4%83r%C4%83%C8%99an"
target="_blank">here</a>.
</p>
</div>

How can I achieve a scrollable circular text effect similar to http://thisiskiosk.com/

If you take a look at KIOSK WEBSITE HERE they have the 'WE ARE OPEN" circular type in javascript (I know how to do that) but what I don't know is how to achieve that when scrolling. Like how does the text move when scrolling up or down. How do you get that in HTML/CSS/JS ?
View the code I worked on here https://codepen.io/noel_emmanuel/pen/WJxRZW
HTML:
<!--just a container used to position in the page-->
<div class="container">
<!--the holders/targets for the text, reuse as desired-->
<div class="circTxt" id="test"></div>
</div>
<!--I told you it was simple! :)-->
CSS:
body {
background: #111;
}
.container {
/*centers in the container*/
text-align: center;
}
div.circTxt {
/*allows for centering*/
display: inline-block;
/*adjust as needed*/
margin-bottom: 128px;
color: whitesmoke;
}
JS:
function circularText(txt, radius, classIndex) {
txt = txt.split(""),
classIndex = document.getElementsByClassName("circTxt")[classIndex];
var deg = 360 / txt.length,
origin = 0;
txt.forEach((ea) => {
ea = `<p style='height:${radius}px;position:absolute;transform:rotate(${origin}deg);transform-origin:0 100%'>${ea}</p>`;
classIndex.innerHTML += ea;
origin += deg;
});
}
circularText("WE ARE OPEN", 100, 0);
OPEN FOR SUGGESTIONS.
You could rotate this on a scroll event. This simply rotates the div depending on how far from the top of the page you have scrolled.
I added a height and width to the text, as well as positioned it fixed to see the effect.
function circularText(txt, radius, classIndex) {
txt = txt.split(""),
classIndex = document.getElementsByClassName("circTxt")[classIndex];
var deg = 360 / txt.length,
origin = 0;
txt.forEach((ea) => {
ea = `<p style='height:${radius}px;position:absolute;transform:rotate(${origin}deg);transform-origin:0 100%'>${ea}</p>`;
classIndex.innerHTML += ea;
origin += deg;
});
}
circularText("WE ARE OPEN", 100, 0);
$(document).ready(function(){
$(window).scroll(function(e){
rotateText();
});
function rotateText(){
var scrolled = $(window).scrollTop();
$('div.circTxt').css('transform','rotate('+scrolled+'deg)');
}
});
body {
background: #111;
}
.container {
/*centers in the container*/
text-align: center;
height: 4000px;
}
div.circTxt {
/*allows for centering*/
display: inline-block;
/*adjust as needed*/
margin-bottom: 128px;
color: whitesmoke;
position: fixed;
height: 200px;
width: 200px;
transform-origin: 0% 59%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!--just a container used to position in the page-->
<div class="container">
<!--the holders/targets for the text, reuse as desired-->
<div class="circTxt" id="test"></div>
</div>
<!--I told you it was simple! :)-->

jQuery - Change background colour on scroll

I want to transition the background colour of a fixed header element on scroll. So as a user scrolls down a full page block website, the header subtly changes to complement the block colours. I have almost achieved this on a Pen, however I can't quite work out how to measure how much has been scrolled as a flag for when to change.
Some extra info: The scroll amount to change at is 400px. The background colours are stored and fetched in an array. For reference my jQuery code is below:
$(document).ready(function(){
var bgArray = ["#252525","#333333","#454545","#777777"];
var scrollHeight = 400;
var scrolled = $(window).scrollTop(); //What is this measuring?
$(window).scroll(function() { //Can these conditions be neatened into one function?
if(scrolled < scrollHeight) {
$('header').css('background', bgArray[0]);
}
if(scrolled > scrollHeight) { // i.e more than 400px
$('header').css('background', bgArray[1]);
}
// and so on (800, 1200...)
})
})
Please refer to the Pen for full code. Any suggestions are greatly appreciated!
Updated Solution (2019)
To set a background for the header based on the current block in view below the header while scrolling:
because header has fixed position, we can get the amount by which window has scrolled by using $header.offset().top,
(index of the current block in view) is the ratio of (the amount by which window has scrolled) to the (height of each block),
now adjusting for the height of the header, the index of the current block in view is Math.floor(($header.offset().top + headerHeight) / sectionHeight).
See simplified demo below:
$(function() {
var $header = $('header'),
$window = $(window),
bgArray = ["#252525", "red", "blue", "green"],
headerHeight = 50,
sectionHeight = 400;
$window.scroll(function() {
$header.css('background', bgArray[Math.floor(($header.offset().top + headerHeight)
/ sectionHeight)]);
});
});
:root {
--header: 50px; /* header height */
--block: 400px; /* block height */
}
* {
box-sizing: border-box; /* include padding in width / height calculations */
}
body {
margin: 0; /* reset default margin of body */
}
header {
height: var(--header); /* sets height of header */
position: fixed;
top: 0;
left: 0;
width: 100%;
color: #FFF;
padding: 12px 0;
background: #252525; /* initial background */
transition: background 1s ease;
}
.container {
margin: 0 auto;
}
.wrap>div {
height: var(--block); /* sets height of each block */
text-align: center;
}
p {
margin: 0; /* reset margin of p */
}
.block-1 {
background: #27AACC;
color: #FFF;
}
.block-2 {
background: #668E99;
color: #FFF;
}
.block-3 {
background: #4AFFC1;
color: #444;
}
.block-4 {
background: #FF8F8A;
color: #FFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header>
<div class="container">
Website Title.
</div>
</header>
<div class="wrap">
<div class="block-1">
<div class="container">
<p>This pen was made to solve a problem on a project...</p>
</div>
</div>
<div class="block-2">
<div class="container">
<p>...I needed a sticky header with thr right bg colour.</p>
</div>
</div>
<div class="block-3">
<div class="container">
<p>But this conflicted with the footer, which was the same colour...</p>
</div>
</div>
<div class="block-4">
<div class="container">
<p>So the solution was to subtley change the header's bg on scroll</p>
</div>
</div>
</div>
Original Solution
Check the top of each block with respect to how much the window has been scrolled (scrollTop) using $(window).scrollTop() > $('.block-1').offset().top. So now we can use this to change color on entering the block - see demo below:
$(document).ready(function() {
var $header = $('header'),
$window = $(window),
bgArray = ["#252525", "#333333", "#454545", "#777777"],
headerHeight = $header.outerHeight();
$window.scroll(function() {
for (var i = 1; i < 5; i++) {
if ($window.scrollTop() + headerHeight > $('.block-' + i).offset().top) {
$header.css('background', bgArray[i - 1]);
}
}
});
});
#import url('https://fonts.googleapis.com/css?family=Roboto:300,400,700');
body {
font-family: 'Roboto', sans-serif;
font-size: 30px;
font-weight: 300;
margin-top: 0;
}
header {
width: 100%;
height: 50px;
line-height: 50px;
position: fixed;
font-size: 24px;
font-weight: 700;
color: #FFF;
padding: 12px 0;
margin: 0;
background: #252525;
transition: background 1s ease;
}
.wrap {
padding-top: 74px;
margin: 0;
}
.container {
width: 960px;
margin: 0 auto;
overflow: hidden;
}
.block-1,
.block-2,
.block-3,
.block-4 {
height: 400px;
text-align: center;
}
p {
margin-top: 185px;
}
.block-1 {
background: #27AACC;
color: #FFF;
}
.block-2 {
background: #668E99;
color: #FFF;
}
.block-3 {
background: #4AFFC1;
color: #444;
}
.block-4 {
background: #FF8F8A;
color: #FFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header>
<div class="container">
Website Title.
</div>
</header>
<div class="wrap">
<div class="block-1">
<div class="container">
<p>This pen was made to solve a problem on a project...</p>
</div>
</div>
<div class="block-2">
<div class="container">
<p>...I needed a sticky header with thr right bg colour.</p>
</div>
</div>
<div class="block-3">
<div class="container">
<p>But this conflicted with the footer, which was the same colour...</p>
</div>
</div>
<div class="block-4">
<div class="container">
<p>So the solution was to subtley change the header's bg on scroll</p>
</div>
</div>
</div>
Note that this solution needlessly loops through the sections on each scroll update called by the browser - and I don't like the look of it.
you are using scrolled as a fixed variable you should use it directly in your condition
this will make it dynamic for all elements inside wrap div
$(document).ready(function(){
var bgArray = ["#252525","#333333","#454545","#777777"];
$(window).scroll(function() {
for(var i = 1; i < bgArray.length; i++) {
if ($(window).scrollTop() > $('.wrap div:nth-child(' + i + ')').offset().top) {
$('header').css('background', bgArray[i-1]);
}
}
});
})
Try Like this:
$(document).ready(function(){
var bgArray = ["#252525","#333333","#454545","#777777"];
var scrollHeight = 400;
$(window).scroll(function() {
var scrolled = $(window).scrollTop();
var index=Number((scrolled/scrollHeight).toFixed());
if(bgArray[index]!=undefined)
$('header').css('background', bgArray[index]);
});
})
This is current scroll, so it should be inside: $(window).scrollTop()

How do you create 3 adjustable divs?

What I want:
| A | | B | | C |
^ ^
When you move the handles left and right A, B, and C resize accordingly
| A | | B | | C |
What I have is the || between B and C sliding, but not resizing B and all I get on the other one is the resize cursor. Basically C is a curtain and covers A and B. I did get min size working for C.
| A | C |
I broke somebody else's perfectly good code to get this far:
var isResizing = false,
who='',
lastDownX = 0;
$(function () {
var container = $('#container'),
left = $('#left'),
right = $('#right'),
middle = $('#middle'),
hand2 = $('#hand2'),
handle = $('#handle');
handle.on('mousedown', function (e) {
isResizing = true;
who=e.target.id;
lastDownX = e.clientX;
});
$(document).on('mousemove', function (e) {
var temp, min;
// we don't want to do anything if we aren't resizing.
if (!isResizing)
return;
min=container.width() * 0.1;
temp = container.width() - (e.clientX - container.offset().left);
if (temp < min)
temp = min;
if (who == 'handle')
right.css('width', temp);
if (who == 'hand2')
left.css('width', temp);
}).on('mouseup', function (e) {
// stop resizing
isResizing = false;
});
});
body, html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#container {
width: 100%;
height: 100%;
/* Disable selection so it doesn't get annoying when dragging. */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: moz-none;
-ms-user-select: none;
user-select: none;
}
#container #left {
width: 40%;
height: 100%;
float: left;
background: red;
}
#container #middle {
margin-left: 40%;
height: 100%;
background: green;
}
#container #right {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 200px;
background: rgba(0, 0, 255, 0.90);
}
#container #handle {
position: absolute;
left: -4px;
top: 0;
bottom: 0;
width: 80px;
cursor: w-resize;
}
#container #hand2 {
position: absolute;
left: 39%;
top: 0;
bottom: 0;
width: 80px;
cursor: w-resize;
}
<div id="container">
<!-- Left side -->
<div id="left"> This is the left side's content!</div>
<!-- middle -->
<div id="middle">
<div id="hand2"></div> This is the middle content!
</div>
<!-- Right side -->
<div id="right">
<!-- Actual resize handle -->
<div id="handle"></div> This is the right side's content!
</div>
</div>
Been playing with it here: https://jsfiddle.net/ju9zb1he/5/
I was looking for a solution that required less extensive CSS. It does have one minor bug(FIXED), but hopefully this should get you started. Here is a DEMO.
Also I aimed to use DOM Traversal methods like .next() and .prev() that way it wouldn't be so attribute dependent, and would be easily reusable if you needed a feature like this multiple times on a page.
Edit - Further Explanation
The idea here is onClick of a .handle we want to gather the total width (var tWidth) of the .prev() and .next() divs relative to the .handle in the DOM. We can then use the start mouse position (var sPos) to substract the amount of pixels we've moved our mouse (e.pageX). Doing so gives us the correct width that the .prev() div should have on mousemove. To get the width of the .next() div we need only to subtract the width of the .prev() div from the total width (var tWidth) that we stored onClick of the .handle. Hope this helps! Let me know if you have any further questions, however I will likely be unavailable till tomorrow.
HTML
<div class="container">
<div id="left"></div>
<div id="l-handle" class="handle"></div>
<div id="middle"></div>
<div id="r-handle" class="handle"></div>
<div id="right"></div>
</div>
CSS
#left, #middle, #right {
display: inline-block;
background: #e5e5e5;
min-height: 200px;
margin: 0px;
}
#l-handle, #r-handle {
display: inline-block;
background: #000;
width: 2px;
min-height: 200px;
cursor: col-resize;
margin: 0px;
}
jQuery
var isDragging = false,
cWidth = $('.container').width(),
sPos,
handle,
tWidth;
$('#left, #middle, #right').width((cWidth / 3) - 7); // Set the initial width of content sections
$('.handle').on('mousedown', function(e){
isDragging = true;
sPos = e.pageX;
handle = $(this);
tWidth = handle.prev().width() + handle.next().width();
});
$(window).on('mouseup', function(e){
isDragging = false;
});
$('.container').on('mousemove', function(e){
if(isDragging){ // Added an additional condition here below
var cPos = sPos - e.pageX;
handle.prev().width((tWidth / 2) - cPos); // This was part of the bug...
handle.next().width(tWidth - handle.prev().width());
// Added an update to sPos here below
}
});
Edit
The bug was caused by 2 things.
1) On mousemove we were dividing the total width by two, instead of an updated mouse offset.
2) The sPos was not updating on mousemove, and stayed a static number based off of the click location.
Resolution
Update the sPos on mousemove that way the mouse offset is accurately based off of the previous mousemove position, rather than the click position. When this is done we can then subtract the .next() div's width from the total width. Then we subtract our current mouse position from the remaining width. The fiddle has been updated as well.
$('.container').on('mousemove', function(e){
var cPos = sPos - e.pageX;
if(isDragging && ((tWidth - handle.next().width()) - cPos) <= tWidth){
handle.prev().width((tWidth - handle.next().width()) - cPos);
handle.next().width(tWidth - handle.prev().width());
sPos = e.pageX;
}
});
Edit
Added an additional condition on mousemove to prevent the drag from exceeding the total width (var tWidth).
Can you please explain what you're trying to accomplish?
I don't believe you need to use position: absolute. The premise of absolute positioning is to override the margin and padding imposed on an element by its parent.
You don't need to do this, all elements have relative positioning by default which makes them push eachother around and don't allow overlapping.
I'm probably missing something, but I think this is what you want with nothing but some very basic CSS: http://jsfiddle.net/3bdoazpk/
<div class='first'>
asdf
</div><div class='second'>
dasdf
</div><div class='third'>
sadf
</div>
body {
margin: 0;
}
div {
display: inline-block;
height: 100%;
}
.first, .third {
width: 40%;
}
.first {
background-color: red;
}
.second {
background-color: blue;
width: 20%;
}
.third {
background-color: green;
}

Categories

Resources