Home > Software design >  Match background color with random color div on scroll-snap using javascript?
Match background color with random color div on scroll-snap using javascript?

Time:12-21

I'm practicing by making this website - https://backstagetalks.com/ I created divs that are scrollable and made it such that each generates a random color.

However I want to know how to use Javascript to identify when a particular div has snapped to the center so that it can set the background color to match the color of that div

I tried to use the scrollX, scrollY and offSetTop, offsetHeight properties. But the divs all have fixed offsetHeight, so it didn't work

I don't know jquery yet and I haven't started learning CSS or JS libraries/frameworks, so I don't know how to use those to help.

This is the Code:

const gra = function (min, max) {
    return Math.random() * (max - min)   min;
};

const init = function () {
    let items = document.querySelectorAll(".scroller div");
    for (let i = 0; i < items.length; i  ) {

        items[i].style.minHeight = gra(100, 100)   "vh";

        const randomColor = Math.floor(Math.random() * 16777215).toString(16);
        items[i].style.backgroundColor = "#"   randomColor;
        let itemColor = items[i].style.backgroundColor;
        // items[i].style.borderColor = "#"   ((1 << 24) * Math.random() | 0).toString(16);
    }
}
init();
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo Mono:ital,wght@1,500&family=Hanalei Fill&family=Fredoka One&display=swap');

html, body{
    margin: 0;
    /* font-family: Arial, Helvetica, sans-serif; */
}
::-webkit-scrollbar {
    display: none;
}

header {
    padding: 0.7em 0 0 0 ;
    color: red;
    font-family: 'Bangers';
    font-size: 25px;
}

header h2 {
    margin: 0;
}

.container {
    height: 100vh;
    display: flex;
    justify-content: space-between;
    /* border: 2px solid brown; */
}

.scroller {
    max-height: 100vh;
    overflow: scroll;
    scroll-snap-type: y mandatory;
    margin: 0 auto;
    width: 100%;
    position: absolute;
    scroll-behavior: smooth;
    z-index: 1;
 
}

.book {
    /* border: 20px solid; */
    margin: 0 auto;
    /* padding: 1em; */
    width: 300px;
    scroll-snap-align: center;
    scroll-padding-top: 300px;
    display: flex;
    flex-flow: column;
    justify-content: center;
    text-align: center;
    font-size: 4em;
}


.links {
    display: flex;
    justify-content: space-between;
    flex-direction: column;
    /* padding: 0 1em; */
}

.links h2 {
    font-family: 'Fredoka One';
    font-weight: 100;
    padding: 0;
}

.issue-list {
    width: fit-content;
    margin-left: auto;
    font-family: 'Arial';
    font-size: 18px;
    z-index: 1;
}

.issue-list p{
    margin: 0;
}

.issue-list p a {
    cursor: pointer;
    text-decoration: none;
    color: black;
}

.info {
    position: absolute;
    bottom: 0;
    /* padding: 0 1em; */
}

.info p {
    font-size: 19px;
    font-weight: bold;
    font-family: 'Chivo Mono';
}
<div id="background" >
    <header>
        <h2>BACKSTAGE TALKS</h2>
    </header>
    <div >
        <p> Backstage Talks is a magazine <br>
            casual, but in depth dialogues <br>
            on design and business. Our decisions <br>
            shape and influence this complex <br>
            world—to have a chance to make the <br>
            right ones, we need to talk </p>
    </div>
    <div id="scroll-box" >
        <div  id="book_issue-1">1</div>
        <div  id="book_issue-2">2</div>
        <div  id="book_issue-3">3</div>
        <div  id="book_issue-4">4</div>
        <div  id="book_issue-5">5</div>
        <div  id="book_issue-6">6</div>
    </div>

    <div >
        <h2>[email protected]</h2>
        <div >
            <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
            <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
            <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
            <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
            <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
            <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
        </div>
    </div>


</div>

CodePudding user response:

You can check when one of your books div has snapped using a javascript function (scrollHandler in this example) like the one from this answer:

https://stackoverflow.com/a/66029649/5334486

Once you know when the "snapped" event happens, you can loop over your books divs and check which one of them is currently displayed testing whether getBoundingClientRect().top equals zero.

Finally, once you know which div is currently snapped, you can assign the same background color to body.

const gra = function(min, max) {
  return Math.random() * (max - min)   min;
};

const init = function() {
  let items = document.querySelectorAll(".scroller div");
  for (let i = 0; i < items.length; i  ) {

    items[i].style.minHeight = gra(100, 100)   "vh";

    const randomColor = Math.floor(Math.random() * 16777215).toString(16);
    items[i].style.backgroundColor = "#"   randomColor;
    let itemColor = items[i].style.backgroundColor;
    // items[i].style.borderColor = "#"   ((1 << 24) * Math.random() | 0).toString(16);
  }
}
init();


function scrollHandler(e) {
  var atSnappingPoint = e.target.scrollTop % e.target.offsetHeight === 0;
  var timeOut = atSnappingPoint ? 0 : 150;

  clearTimeout(e.target.scrollTimeout);

  e.target.scrollTimeout = setTimeout(function() {
    //using the timeOut to evaluate scrolling state
    if (!timeOut) {
      console.log('Scroller snapped!');
      document.querySelectorAll(".scroller div").forEach(theDiv => {
         if (theDiv.id, theDiv.getBoundingClientRect().top == 0) {
             // THIS IS THE CURRENTLY SNAPPED BOOK
             console.log(theDiv.id, theDiv.style.backgroundColor)
             document.querySelector('body').style.backgroundColor = theDiv.style.backgroundColor
         }
      });
    }
  }, timeOut);
}



document.querySelector(".scroller").addEventListener('scroll', scrollHandler);
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo Mono:ital,wght@1,500&family=Hanalei Fill&family=Fredoka One&display=swap');
html,
body {
  margin: 0;
  /* font-family: Arial, Helvetica, sans-serif; */
}

::-webkit-scrollbar {
  display: none;
}

header {
  padding: 0.7em 0 0 0;
  color: red;
  font-family: 'Bangers';
  font-size: 25px;
}

header h2 {
  margin: 0;
}

.container {
  height: 100vh;
  display: flex;
  justify-content: space-between;
  /* border: 2px solid brown; */
}

.scroller {
  max-height: 100vh;
  overflow: scroll;
  scroll-snap-type: y mandatory;
  margin: 0 auto;
  width: 100%;
  position: absolute;
  scroll-behavior: smooth;
  z-index: 1;
}

.book {
  /* border: 20px solid; */
  margin: 0 auto;
  /* padding: 1em; */
  width: 300px;
  scroll-snap-align: center;
  scroll-padding-top: 300px;
  display: flex;
  flex-flow: column;
  justify-content: center;
  text-align: center;
  font-size: 4em;
}

.links {
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  /* padding: 0 1em; */
}

.links h2 {
  font-family: 'Fredoka One';
  font-weight: 100;
  padding: 0;
}

.issue-list {
  width: fit-content;
  margin-left: auto;
  font-family: 'Arial';
  font-size: 18px;
  z-index: 1;
}

.issue-list p {
  margin: 0;
}

.issue-list p a {
  cursor: pointer;
  text-decoration: none;
  color: black;
}

.info {
  position: absolute;
  bottom: 0;
  /* padding: 0 1em; */
}

.info p {
  font-size: 19px;
  font-weight: bold;
  font-family: 'Chivo Mono';
}
<div id="background" >
  <header>
    <h2>BACKSTAGE TALKS</h2>
  </header>
  <div >
    <p> Backstage Talks is a magazine <br> casual, but in depth dialogues <br> on design and business. Our decisions <br> shape and influence this complex <br> world—to have a chance to make the <br> right ones, we need to talk </p>
  </div>
  <div id="scroll-box" >
    <div  id="book_issue-1">1</div>
    <div  id="book_issue-2">2</div>
    <div  id="book_issue-3">3</div>
    <div  id="book_issue-4">4</div>
    <div  id="book_issue-5">5</div>
    <div  id="book_issue-6">6</div>
  </div>

  <div >
    <h2>[email protected]</h2>
    <div >
      <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
      <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
      <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
      <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
      <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
      <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
    </div>
  </div>


</div>

CodePudding user response:

You can check through the scroll positions of the divs individually and attach a srcoll event listener on the .scroll-box, then assign the colors based on the scroll position.

const gra = function (min, max) {
    return Math.random() * (max - min)   min;
};

const init = function () {
    let items = document.querySelectorAll(".scroller div");
    let positions = [];
    let itemcolors = [];
    for (let i = 0; i < items.length; i  ) {
        let item = items[i];
        
        item.style.minHeight = gra(100, 100)   "vh";
        const randomColor = Math.floor(Math.random() * 16777215).toString(16);
        item.style.backgroundColor = "#"   randomColor;
        let itemColor = item.style.backgroundColor;
        
        // Arrays to hold colors and position in same index positions
        itemcolors.push(itemColor)
        positions.push(item.offsetTop)
        /* on scrolling, get the offset and check against the existing
        scroll positions of the divs */
        
        document.getElementById('scroll-box').addEventListener('scroll', function(){
            let scrolloffset = document.getElementById('scroll-box').scrollTop
            positions.forEach((position, index) => {
                // Give an allowance of   or - 5 for padding and other spacing factors
                if(position >= (scrolloffset-5) && position <= (scrolloffset 5)){
                    document.getElementById('background').style.backgroundColor = itemcolors[index];
                }
            })
        })
        
        // items[i].style.borderColor = "#"   ((1 << 24) * Math.random() | 0).toString(16);
    }
    
}
init();
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo Mono:ital,wght@1,500&family=Hanalei Fill&family=Fredoka One&display=swap');

html, body{
    margin: 0;
    /* font-family: Arial, Helvetica, sans-serif; */
}
::-webkit-scrollbar {
    display: none;
}

header {
    padding: 0.7em 0 0 0 ;
    color: red;
    font-family: 'Bangers';
    font-size: 25px;
}

header h2 {
    margin: 0;
}

.container {
    height: 100vh;
    display: flex;
    justify-content: space-between;
    /* border: 2px solid brown; */
}

.scroller {
    max-height: 100vh;
    overflow: scroll;
    scroll-snap-type: y mandatory;
    margin: 0 auto;
    width: 100%;
    position: absolute;
    scroll-behavior: smooth;
    z-index: 1;
 
}

.book {
    /* border: 20px solid; */
    margin: 0 auto;
    /* padding: 1em; */
    width: 300px;
    scroll-snap-align: center;
    scroll-padding-top: 300px;
    display: flex;
    flex-flow: column;
    justify-content: center;
    text-align: center;
    font-size: 4em;
}


.links {
    display: flex;
    justify-content: space-between;
    flex-direction: column;
    /* padding: 0 1em; */
}

.links h2 {
    font-family: 'Fredoka One';
    font-weight: 100;
    padding: 0;
}

.issue-list {
    width: fit-content;
    margin-left: auto;
    font-family: 'Arial';
    font-size: 18px;
    z-index: 1;
}

.issue-list p{
    margin: 0;
}

.issue-list p a {
    cursor: pointer;
    text-decoration: none;
    color: black;
}

.info {
    position: absolute;
    bottom: 0;
    /* padding: 0 1em; */
}

.info p {
    font-size: 19px;
    font-weight: bold;
    font-family: 'Chivo Mono';
}
<div id="background" >
    <header>
        <h2>BACKSTAGE TALKS</h2>
    </header>
    <div >
        <p> Backstage Talks is a magazine <br>
            casual, but in depth dialogues <br>
            on design and business. Our decisions <br>
            shape and influence this complex <br>
            world—to have a chance to make the <br>
            right ones, we need to talk </p>
    </div>
    <div id="scroll-box" >
        <div  id="book_issue-1">1</div>
        <div  id="book_issue-2">2</div>
        <div  id="book_issue-3">3</div>
        <div  id="book_issue-4">4</div>
        <div  id="book_issue-5">5</div>
        <div  id="book_issue-6">6</div>
    </div>

    <div >
        <h2>[email protected]</h2>
        <div >
            <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
            <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
            <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
            <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
            <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
            <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
        </div>
    </div>


</div>

CodePudding user response:

You can test if an element is (completly) inside the viewport by using Element.getBoundingClientRect() and compare the values to the window w/h.

const gra = function(min, max) {
  return Math.random() * (max - min)   min;
};

const init = function() {
  let items = document.querySelectorAll(".scroller div");
  for (let i = 0; i < items.length; i  ) {

    items[i].style.minHeight = gra(100, 100)   "vh";

    const randomColor = ('00000'   Math.floor(Math.random() * 16777216).toString(16)).substr(-6);
    items[i].style.backgroundColor = "#"   randomColor;
    let itemColor = items[i].style.backgroundColor;
    // items[i].style.borderColor = "#"   ((1 << 24) * Math.random() | 0).toString(16);
  }
}
init();

function isInViewport(element) {
  const { top, left, bottom, right } = element.getBoundingClientRect();
  return (
    top >= 0 &&
    left >= 0 &&
    bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}
const scroller = document.querySelector(".scroller");

scroller.addEventListener("scroll", ({ target }) => {
  [...target.childNodes]
    .filter(child => child.nodeType != 3 && isInViewport(child))
    .forEach(child => document.body.style.backgroundColor = child.style.backgroundColor);
});
/* initial */
scroller.dispatchEvent(new Event("scroll"));
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo Mono:ital,wght@1,500&family=Hanalei Fill&family=Fredoka One&display=swap');
html,
body {
  margin: 0;
  /* font-family: Arial, Helvetica, sans-serif; */
}

::-webkit-scrollbar {
  display: none;
}

header {
  padding: 0.7em 0 0 0;
  color: red;
  font-family: 'Bangers';
  font-size: 25px;
}

header h2 {
  margin: 0;
}

.container {
  height: 100vh;
  display: flex;
  justify-content: space-between;
  /* border: 2px solid brown; */
}

.scroller {
  max-height: 100vh;
  overflow: scroll;
  scroll-snap-type: y mandatory;
  margin: 0 auto;
  width: 100%;
  position: absolute;
  scroll-behavior: smooth;
  z-index: 1;
}

.book {
  /* border: 20px solid; */
  margin: 0 auto;
  /* padding: 1em; */
  width: 300px;
  scroll-snap-align: center;
  scroll-padding-top: 300px;
  display: flex;
  flex-flow: column;
  justify-content: center;
  text-align: center;
  font-size: 4em;
}

.links {
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  /* padding: 0 1em; */
}

.links h2 {
  font-family: 'Fredoka One';
  font-weight: 100;
  padding: 0;
}

.issue-list {
  width: fit-content;
  margin-left: auto;
  font-family: 'Arial';
  font-size: 18px;
  z-index: 1;
}

.issue-list p {
  margin: 0;
}

.issue-list p a {
  cursor: pointer;
  text-decoration: none;
  color: black;
}

.info {
  position: absolute;
  bottom: 0;
  /* padding: 0 1em; */
}

.info p {
  font-size: 19px;
  font-weight: bold;
  font-family: 'Chivo Mono';
}
<div id="background" >
  <header>
    <h2>BACKSTAGE TALKS</h2>
  </header>
  <div >
    <p> Backstage Talks is a magazine <br> casual, but in depth dialogues <br> on design and business. Our decisions <br> shape and influence this complex <br> world—to have a chance to make the <br> right ones, we need to talk </p>
  </div>
  <div id="scroll-box" >
    <div  id="book_issue-1">1</div>
    <div  id="book_issue-2">2</div>
    <div  id="book_issue-3">3</div>
    <div  id="book_issue-4">4</div>
    <div  id="book_issue-5">5</div>
    <div  id="book_issue-6">6</div>
  </div>

  <div >
    <h2>[email protected]</h2>
    <div >
      <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
      <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
      <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
      <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
      <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
      <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
    </div>
  </div>


</div>

There is also a more modern way with the Intersection Observer API.

const gra = function(min, max) {
  return Math.random() * (max - min)   min;
};

const init = function() {
  let items = document.querySelectorAll(".scroller div");
  for (let i = 0; i < items.length; i  ) {

    items[i].style.minHeight = gra(100, 100)   "vh";

    const randomColor = ('00000'   Math.floor(Math.random() * 16777216).toString(16)).substr(-6);
    items[i].style.backgroundColor = "#"   randomColor;
    let itemColor = items[i].style.backgroundColor;
    // items[i].style.borderColor = "#"   ((1 << 24) * Math.random() | 0).toString(16);
  }
}
init();

const options = {
  root: null,
  rootMargin: "0px",
  threshold: [0.51] // 51% visible
};

const observer = new IntersectionObserver((list) => {
  list.forEach(entry => {
      if (entry.intersectionRatio >= 0.51) {
        document.body.style.backgroundColor = entry.target.style.backgroundColor;
      }
  })
}, options);
/* observe every child except text nodes */
document.querySelector(".scroller").childNodes.forEach(child => child.nodeType != 3 && observer.observe(child));
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo Mono:ital,wght@1,500&family=Hanalei Fill&family=Fredoka One&display=swap');
html,
body {
  margin: 0;
  /* font-family: Arial, Helvetica, sans-serif; */
}

::-webkit-scrollbar {
  display: none;
}

header {
  padding: 0.7em 0 0 0;
  color: red;
  font-family: 'Bangers';
  font-size: 25px;
}

header h2 {
  margin: 0;
}

.container {
  height: 100vh;
  display: flex;
  justify-content: space-between;
  /* border: 2px solid brown; */
}

.scroller {
  max-height: 100vh;
  overflow: scroll;
  scroll-snap-type: y mandatory;
  margin: 0 auto;
  width: 100%;
  position: absolute;
  scroll-behavior: smooth;
  z-index: 1;
}

.book {
  /* border: 20px solid; */
  margin: 0 auto;
  /* padding: 1em; */
  width: 300px;
  scroll-snap-align: center;
  scroll-padding-top: 300px;
  display: flex;
  flex-flow: column;
  justify-content: center;
  text-align: center;
  font-size: 4em;
}

.links {
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  /* padding: 0 1em; */
}

.links h2 {
  font-family: 'Fredoka One';
  font-weight: 100;
  padding: 0;
}

.issue-list {
  width: fit-content;
  margin-left: auto;
  font-family: 'Arial';
  font-size: 18px;
  z-index: 1;
}

.issue-list p {
  margin: 0;
}

.issue-list p a {
  cursor: pointer;
  text-decoration: none;
  color: black;
}

.info {
  position: absolute;
  bottom: 0;
  /* padding: 0 1em; */
}

.info p {
  font-size: 19px;
  font-weight: bold;
  font-family: 'Chivo Mono';
}
<div id="background" >
  <header>
    <h2>BACKSTAGE TALKS</h2>
  </header>
  <div >
    <p> Backstage Talks is a magazine <br> casual, but in depth dialogues <br> on design and business. Our decisions <br> shape and influence this complex <br> world—to have a chance to make the <br> right ones, we need to talk </p>
  </div>
  <div id="scroll-box" >
    <div  id="book_issue-1">1</div>
    <div  id="book_issue-2">2</div>
    <div  id="book_issue-3">3</div>
    <div  id="book_issue-4">4</div>
    <div  id="book_issue-5">5</div>
    <div  id="book_issue-6">6</div>
  </div>

  <div >
    <h2>[email protected]</h2>
    <div >
      <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
      <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
      <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
      <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
      <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
      <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
    </div>
  </div>


</div>

  • Related