Hook
Hook

Reputation: 460

Check if element is between 30% and 60% of the viewport

I am trying to change the color of <li> elements when they are between 30% and 60% of the viewport.

So I have this grid of elements stacking side by side like this:

elements stacking side by side

I ran into few plugins like Waypoints, Viewport Checker and few others but nothing good.

Any idea?

I am using a pretty simple structure :

JSFIDDLE

HTML

<!doctype html>

<html lang="en">

<head>
    <meta charset="utf-8">

    <title></title>
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/styles.css">
    <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
    <script src="js/main.js"></script>
    <!--[if lt IE 9]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->
</head>

<body>
    <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</body>

</html>

CSS:

ul {
    margin: auto;
}
ul li {
    width: 300px;
    height: 200px;
    background: #f5f5f5;
    float: left;
    margin: 10px;
}

ul li.middleviewport{
    background:red;
}

Upvotes: 36

Views: 20240

Answers (5)

Andrea Ligios
Andrea Ligios

Reputation: 50203

Improved @Tushar's solution to make it work even after a resize of the window (a recalculation of the viewport is necessary each time, not only at the beginning), and to make it start already highlighted, without the need to scroll.

Also improved a bit the graphic of the example to highlight the interested area.

Running demo

$(document).ready(function() {
  $(window).on('scroll', function() {
    var windowHeight = $(window).height(),
      gridTop = windowHeight * .3,
      gridBottom = windowHeight * .6;
    $('ul li').each(function() {
      var thisTop = $(this).offset().top - $(window).scrollTop();

      if (thisTop > gridTop && (thisTop + $(this).height()) < gridBottom) {
        $(this).css('background', 'red');
      } else {
        $(this).css('background', 'silver');
      }
    });

  });
  $(window).trigger('scroll');
});
ul {
  margin: auto;
}
ul li {
  width: 300px;
  height: 10px;
  background: silver;
  float: left;
  margin: 10px;
  list-style: none;
}
ul li.middleviewport {
  background: red;
}
#viewportMask {
  position: fixed;
  top: 30%;
  bottom: 40%;
  left: 0;
  right: 0;
  background: red;
  opacity: 0.2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="viewportMask"></div>
<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

Upvotes: 9

Tushar
Tushar

Reputation: 87203

  1. Use scroll event handler on window
  2. Loop over all the li elements to check if the element is in the interested viewport
  3. Get the li position from top and check if it is in the interested viewport section.

Demo:

Changed the height of li for demo purpose.

See the comments inline in the code.

$(document).ready(function() {
  // Get viewport height, gridTop and gridBottom
  var windowHeight = $(window).height(),
    gridTop = windowHeight * .3,
    gridBottom = windowHeight * .6;

  $(window).on('scroll', function() {
    // On each scroll check if `li` is in interested viewport
    $('ul li').each(function() {
      var thisTop = $(this).offset().top - $(window).scrollTop(); // Get the `top` of this `li`

      // Check if this element is in the interested viewport
      if (thisTop >= gridTop && (thisTop + $(this).height()) <= gridBottom) {
        $(this).css('background', 'red');
      } else {
        $(this).css('background', 'gray');
      }
    });
  });
});
ul {
  margin: 0;
  list-style-type: none;
  padding: 0;
}
ul li {
  width: 50px;
  height: 30px;
  background: #f5f5f5;
  float: left;
  margin: 10px;
  text-align: center;
  padding-top: 10px
}
ul li.middleviewport {
  background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
  <li>11</li>
  <li>12</li>
  <li>13</li>
  <li>14</li>
  <li>15</li>
  <li>16</li>
  <li>17</li>
  <li>18</li>
  <li>19</li>
  <li>20</li>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
  <li>11</li>
  <li>12</li>
  <li>13</li>
  <li>14</li>
  <li>15</li>
  <li>16</li>
  <li>17</li>
  <li>18</li>
  <li>19</li>
  <li>20</li>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
  <li>11</li>
  <li>12</li>
  <li>13</li>
  <li>14</li>
  <li>15</li>
  <li>16</li>
  <li>17</li>
  <li>18</li>
  <li>19</li>
  <li>20</li>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
  <li>11</li>
  <li>12</li>
  <li>13</li>
  <li>14</li>
  <li>15</li>
  <li>16</li>
  <li>17</li>
  <li>18</li>
  <li>19</li>
  <li>20</li>
</ul>

Upvotes: 32

Salman Arshad
Salman Arshad

Reputation: 272096

[[ This example checks if ANY part of the element is inside the specified region ]]

When you have the top and bottom coordinates of two boxes, you can check if the two boxes overlap by checking:

box1.top < box2.bottom && box1.bottom > box2.top

In the following example, box1 is the 30%-60% portion of window while box2 is each list item. Add debounce function and we have:

var timeout;
$(window).on("load scroll resize", function() {
  if (timeout) {
    clearTimeout(timeout);
  }
  timeout = setTimeout(function() {
    var $window = $(window),
      hitbox_top = $window.scrollTop() + $window.height() * .3,
      hitbox_bottom = $window.scrollTop() + $window.height() * .6;
    $("li").each(function() {
      var $element = $(this),
        element_top = $element.offset().top,
        element_bottom = $element.offset().top + $element.height();
      $element.toggleClass("middle-viewport", hitbox_top < element_bottom && hitbox_bottom > element_top);
    });
  }, 200);
});
#overlay {
  position: fixed;
  left: 0;
  top: 30%;
  width: 100%;
  height: 30%;
  background-color: rgba(0, 192, 255, .5);
}
ul {
  padding: 0;
  text-align: center;
}
li {
  display: inline-block;
  margin: 10px;
  width: 200px;
  height: 200px;
  background-color: #F5F5F5;
}
li.middle-viewport {
  background-color: #FF0000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="overlay"></div>
<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

Upvotes: 2

skobaljic
skobaljic

Reputation: 9614

I was free to create a plugin for this. Within options you can set percents, css class, trigger event and execution delay (css changed to responsive for presentation only):

jQuery.fn.extend({
	markInViewport: function (options) {
		var that = this;
		this.defaults = {
			percentTop: 30,
			percentBottom: 40,
			cssClass: 'middleviewport',
			event: 'scroll resize',
			delay: 10
		};
		this.options = $.extend(that.defaults, options);
		this.win = $(window);
		this.delayChecking = null;
		this.items = [];
		this.checkItems = function (items) {
			clearTimeout(that.delayChecking);
			that.delayChecking = setTimeout(function () {
				var thisWindowHeight = that.win.height();
				var thisWindowScrollTop = that.win.scrollTop();
				that.items.each(function (j) {
					var thisItem = $(this);
					var thisItemHeight = thisItem.outerHeight();
					var thisItemPositionTop = thisItem.offset().top;
					var currentPercentTop = (thisItemPositionTop - thisWindowScrollTop) / thisWindowHeight * 100;
					var currentPercentBottom = (thisWindowScrollTop + thisWindowHeight - thisItemPositionTop - thisItemHeight) / thisWindowHeight * 100;
					thisItem.toggleClass(that.options.cssClass, currentPercentTop >= that.options.percentTop && currentPercentBottom >= that.options.percentBottom);
				});
			}, that.options.delay);
		};
		return this.each(function () {
			that.items = that.children();
			$(window).on(that.options.event, that.checkItems);
			that.checkItems();
		});
	}
});
$('.check_viewport').markInViewport();
ul {
    margin: 0 auto;
    padding: 0;
}
ul li {
    width: 32.73%;
    height: 0;
    padding-bottom: 3.5%; /* responsive height */
    background: #f5f5f5;
    float: left;
    margin: .3%;
    list-style:none;
}
ul li.middleviewport {
    background:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
    <ul class="check_viewport">
        <li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li>
    </ul>
</body>

Fiddle here

Upvotes: 1

Vinc199789
Vinc199789

Reputation: 1046

Create a div width width:100% and height:100% that represent the viewport. Inside this div you place your grid system.

Than you need to use jquery .position() jquery position

var grid = $( "griddiv's" );
var position = grid.position();
var height = $('parentdiv').height();
lower = 0.3 * height;
upper = 0.6 * height;

if(grid.top >= lower && grid.top <= upper){
  $('gridcell').css('background','red');
}

I didn't test it but I hope it works

Upvotes: 0

Related Questions