1

I'm trying to change the background-color of my body with jQuery, when a specific div (in my example the red one) is visible while scrolling. If the div is not visible, the background-color should change again with an animation. I tried follow, but it's not working. Here's also a codepen snippet: https://codepen.io/STWebtastic/pen/qpKdeo

$(window).scroll(function(){
  $('.m-box--red').each(function(){
    if(isScrolledIntoView($(this)) ){
      $("html body").animate({ backgroundColor: "red" }, 300);
  console.log('hello');
    }
    else{
      $("html body").animate({ backgroundColor: "white" }, 300);
  console.log('hello');
    }
  });
});

function isScrolledIntoView(elem){
    var $elem = $(elem);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
    var docViewBottom = docViewTop + $window.height();

    var elemTop = $elem.offset().top;
    var elemBottom = elemTop + $elem.height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
body {
  box-sizing: border-box;
  margin: 0;
}

.m-box {
  display: flex;
  align-items: center;
  padding: 10px;
  background-color: #75989F;
  margin-bottom: 100px;
  cursor: pointer;
  color: white;
  transition: background-color 0.3s;
  height: 300px;
}

.m-box--second {
  background-color: #68808E;
}

.m-box--third {
  background-color: #CDC2AA;
  color: gray;
}

.m-box--red {
  background-color: #D29B8E;
  border: 1px solid lightgray;
}

.m-box__text {
  font-size: 45px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

<div class="m-box m-box--second">
  <p class="m-box__text">Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt. Sed porttitor lectus nibh. Praesent sapien massa, convallis a pellentesque
    nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--red">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--third">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>
webta.st.ic
  • 4,781
  • 6
  • 48
  • 98
  • I would suggest rewriting this heavily so that the browser doesn't have to do all that processing work on the scroll event. I would remove as much of the code _outside_ of the scroll event handler (such as figuring out the element height). Also, if you know the class of the box you want to check for in-viewport, then why loop through _all_ the boxes? That's unnecessary. – JakeParis Jan 12 '18 at 16:54

5 Answers5

2

One problem could be this :

jQuery animate backgroundColor

And the other problem is that you loop over all the div so the body background will get the color of the condition which is in the last item of the loop. So in your case, always white.

So i think you could just addClass with a transition on the background property and only test the red div.

Working codepen :

https://codepen.io/Alvan/pen/rpKLjY?editors=1111

$(window).scroll(function(){
  $('.m-box--red').each(function(){
    if(isScrolledIntoView($(this)) ){
      $("body").addClass('red');
    }
    else{
      $("body").removeClass('red');
    }
  });
});

function isScrolledIntoView(elem){
    var $elem = $(elem);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
    var docViewBottom = docViewTop + $window.height();

    var elemTop = $elem.offset().top;
    var elemBottom = elemTop + $elem.height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
body {
  box-sizing: border-box;
  margin: 0;
  background: white;
  transition: background .3s;
}

body.red {
  background: red;
}

.m-box {
  display: flex;
  align-items: center;
  padding: 10px; 
  background-color: #75989F;
  margin-bottom: 100px;
  cursor: pointer;
  color: white;
  transition: background-color 0.3s;
  height: 150px;
}
.m-box:hover {
    background-color: lighten(#75989F, 10);
  }
  
.m-box--second {
    background-color: #68808E;
}
.m-box--second hover {
    background-color: lighten(#68808E, 10);
}

  
.m-box--third {
    background-color: #CDC2AA;
    color: gray;
}

.m-box--third:hover {
   background-color: lighten(#CDC2AA, 10);
}
  
.m-box--red {
    background-color: #D29B8E;
    border: 1px solid lightgray;
}

.m-box--red :hover {
    background-color: lighten(#D29B8E, 10);
}
  
  
.m-box__text {
  font-size: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

<div class="m-box m-box--second">
  <p class="m-box__text">Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt. Sed porttitor lectus nibh. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--red">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--third">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

If you want the function to work when the top appears at the screen until the bottom disappear from the screen change the condition in function isScrolledIntoView to

if(elemTop <= docViewBottom && elemBottom >= docViewTop) {
    return true;
}

$(window).scroll(function(){
  $('.m-box--red').each(function(){
    if(isScrolledIntoView($(this)) ){
      $("body").addClass('red');
    }
    else{
      $("body").removeClass('red');
    }
  });
});

function isScrolledIntoView(elem){
    var $elem = $(elem);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
    var docViewBottom = docViewTop + $window.height();
  
    var elemTop = $elem.offset().top;
    var elemBottom = elemTop + $elem.height();
  
    if(elemTop <= docViewBottom && elemBottom >= docViewTop) {
      return true;
    }
}
body {
  box-sizing: border-box;
  margin: 0;
  background: white;
  transition: background .3s;
}

body.red {
  background: red;
}

.m-box {
  display: flex;
  align-items: center;
  padding: 10px; 
  background-color: #75989F;
  margin-bottom: 100px;
  cursor: pointer;
  color: white;
  transition: background-color 0.3s;
  height: 150px;
}
.m-box:hover {
    background-color: lighten(#75989F, 10);
  }
  
.m-box--second {
    background-color: #68808E;
}
.m-box--second hover {
    background-color: lighten(#68808E, 10);
}

  
.m-box--third {
    background-color: #CDC2AA;
    color: gray;
}

.m-box--third:hover {
   background-color: lighten(#CDC2AA, 10);
}
  
.m-box--red {
    background-color: #D29B8E;
    border: 1px solid lightgray;
}

.m-box--red :hover {
    background-color: lighten(#D29B8E, 10);
}
  
  
.m-box__text {
  font-size: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

<div class="m-box m-box--second">
  <p class="m-box__text">Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt. Sed porttitor lectus nibh. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--red">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--third">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>
Alexis Vandepitte
  • 2,077
  • 2
  • 12
  • 28
  • Nice, seems to work thanks. What do I have to change, so that the animation starts even if just some part of the div is visible? At the moment the animation starts when the whole div is visible. But I would like to animate the color as soon as possible when the div is coming up and remove it when every part of the div is invisible? Thanks anyway. – webta.st.ic Jan 12 '18 at 16:46
  • Let me 2 minutes to figure it out – Alexis Vandepitte Jan 12 '18 at 16:47
  • I added the answer for you request on my post. Please check the second code snippet – Alexis Vandepitte Jan 12 '18 at 17:00
1

You should use jQuery's .is and :visible selectors to do this:

const $scrollTarget = $(".m-box");
const $body = $("body");
const $window = $(window);

$window.on("scroll", function() {
    return $scrollTarget.is(":visible") ? $body.addClass("element-visible-class") : $body.removeClass("element-visible-class");
});

Where element-visible-class is the class you want to apply when the element is visible.

user1429980
  • 6,872
  • 2
  • 43
  • 53
  • According to the jQuery documentation : Elements are considered visible if they consume space in the document. Visible elements have a width or height that is greater than zero. This is not what he want; he wants to know the element is visible on the screen. Not if it has display none or somethig – Alexis Vandepitte Jan 12 '18 at 16:46
  • This answer properly addresses the main problem, which is that the OP doesn't have any `scroll` event handler set up. – JakeParis Jan 12 '18 at 16:46
1

I made some changes to your code, but I think this is the solution you are looking for:

$(window).scroll(function() {
   var hT = $('.m-box--red').offset().top,
       hH = $('.m-box--red').outerHeight(),       
       wH = $(window).height(),
       wS = $(this).scrollTop();  
   if (wS >= (hT+hH) || wS < (hT+hH-wH)){      
       $("body").css("background-color", "white");
   }   
   else
   if (wS > (hT+hH-wH))
   {
       $("body").css("background-color", "red");
   }   
});
body {
  box-sizing: border-box;
  margin: 0;
  transition: background-color .5s;
}

.m-box {
  display: flex;
  align-items: center;
  padding: 10px;
  background-color: #75989F;
  margin-bottom: 100px;
  cursor: pointer;
  color: white;
  transition: background-color 0.3s;
  height: 300px;
}
.m-box:hover {
  background-color: #93aeb4;
}
.m-box--second {
  background-color: #68808E;
}
.m-box--second:hover {
  background-color: #8499a5;
}
.m-box--third {
  background-color: #CDC2AA;
  color: gray;
}
.m-box--third:hover {
  background-color: #e0d9ca;
}
.m-box--red {
  background-color: #D29B8E;
  border: 1px solid lightgray;
}
.m-box--red:hover {
  background-color: #e1bbb2;
}
.m-box__text {
  font-size: 45px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

<div class="m-box m-box--second">
  <p class="m-box__text">Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt. Sed porttitor lectus nibh. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--red">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--third">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

You just have to add

transition: background-color .5s;

to the body in your css.

parrigin777
  • 178
  • 1
  • 1
  • 10
  • No that's not what im looking for. The body background has to be white in every case, expected when the red div is visible, than it should change the background-color of the body. Your code changes the body to the div, which is visible at the moment. This is no my question. Read the answer carefully. – webta.st.ic Jan 12 '18 at 17:00
  • Oh, sorry about that. I have edited the answer, let me know if this is correct now. – parrigin777 Jan 12 '18 at 17:19
0

according to jquery doc (http://api.jquery.com/animate/) :

All animated properties should be animated to a single numeric value, except as noted below; most properties that are non-numeric cannot be animated using basic jQuery functionality (For example, width, height, or left can be animated but background-color cannot be, unless the jQuery.Color plugin is used).

If you don't want to use this plugin, you can achieve this by completing your css and adding a class to your body when you need

$(window).scroll(function(){
  $('.m-box--red').each(function(){
  // change the selector cause you only need this on m-box--red
 $("html body").toggleClass("red",isScrolledIntoView($(this)));
 // check if the condition is true then add class red, else remove it
  });
});

function isScrolledIntoView(elem){
    var $elem = $(elem);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
 // also chnage this
    var docViewBottom = docViewTop + $("body").height();

    var elemTop = $elem.offset().top;
    var elemBottom = elemTop + $elem.height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
body {
  box-sizing: border-box;
  margin: 0;
    transition: background-color 0.3s;

}

body.red{
  background-color:red;
}

.m-box {
  display: flex;
  align-items: center;
  padding: 10px;
  background-color: #75989F;
  margin-bottom: 100px;
  cursor: pointer;
  color: white;
  transition: background-color 0.3s;
  height: 300px;
}

.m-box--second {
  background-color: #68808E;
}

.m-box--third {
  background-color: #CDC2AA;
  color: gray;
}

.m-box--red {
  background-color: #D29B8E;
  border: 1px solid lightgray;
}

.m-box__text {
  font-size: 45px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

<div class="m-box m-box--second">
  <p class="m-box__text">Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt. Sed porttitor lectus nibh. Praesent sapien massa, convallis a pellentesque
    nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--red">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--third">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>
scraaappy
  • 2,830
  • 2
  • 19
  • 29
0

I would suggest a few changes to better optimize your code (and fix it at the same time!):

  1. Make css animate the background color for you, as that is what css if for.
  2. Move a lot of the processing you're currently doing inside the scroll event handler, outside to minimize what needs to be done on that event. This will prevent stutterly scrolling, or "jank".

Number one can be done by simply adding this to your css:

body {
  transition: background-color .3s;
}

and then changing your javascript from using the animate() jquery function to simply changing the css property like so:

$('body').css('background-color','red');

The css transition property will then natively handle the color transition for you. In fact, an even better way to do it would be to have the javascript simply add/remove a class and have the css handle what that class means for the styling. Then you can use css to control what styles are applied to this special state of the body tag -- which is where your styles should be.

Here is my rewrite of your code working towards these improvements (you need to view the snippet as full screen in order for the effect to work):

(function(){

  var $window = $(window);
  var $elem = $('.m-box--red');
  var elemTop = $elem.offset().top;
  var elemBottom = elemTop + $elem.height();

  $(window).scroll(function(){

    if( isScrolledIntoView( $elem ))
       $('body').addClass('js-active');
    else
       $('body').removeClass('js-active');

  });

  function isScrolledIntoView($elem){

      var docViewTop = $window.scrollTop();
      var docViewBottom = docViewTop + $window.height();

      return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
    }

})();
body {
  box-sizing: border-box;
  margin: 0;
  transition: background-color .3s;
}
body.js-active {
  background-color: red;
}

.m-box {
  display: flex;
  align-items: center;
  padding: 10px;
  background-color: #75989F;
  margin-bottom: 100px;
  cursor: pointer;
  color: white;
  transition: background-color 0.3s;
  height: 300px;
}
.m-box:hover {
  background-color: #93aeb4;
}
.m-box--second {
  background-color: #68808E;
}
.m-box--second:hover {
  background-color: #8499a5;
}
.m-box--third {
  background-color: #CDC2AA;
  color: gray;
}
.m-box--third:hover {
  background-color: #e0d9ca;
}
.m-box--red {
  background-color: #D29B8E;
  border: 1px solid lightgray;
}
.m-box--red:hover {
  background-color: #e1bbb2;
}
.m-box__text {
  font-size: 45px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>

<div class="m-box m-box--second">
  <p class="m-box__text">Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt. Sed porttitor lectus nibh. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--red">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box m-box--third">
  <p class="m-box__text">Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.</p>
</div>

<div class="m-box">
  <p class="m-box__text">Quisque velit nisi, pretium ut lacinia in, elementum id enim.</p>
</div>
JakeParis
  • 11,056
  • 3
  • 42
  • 65