0

On click on the html body: if element '#cap' is viewable, do something. if element '#cap' is not viewable due to being scrolled past it, do something else.

I tried @Heretic Monkey's suggestion and moved the functions to the bottom of the js file but that didn't work at all.

As requested by the moderators I am stating that I tried @Heretic Monkey's suggestion and moved the functions to the bottom of the js file but that didn't work at all.

<thead id = 'cap'>

function isInViewport(el) {
  rect = el.getBoundingClientRect();
  return (rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight ||
    document.documentElement.clientHeight) && rect.right <= (window.innerWidth ||
    document.documentElement.clientWidth));
}

box = document.querySelector('#cap');
document.addEventListener('click', function() {
  if (isInViewport(box)) {
    alert('true');
  } else {
    alert('false');
  }
});

Chrome console says: blank.js:3 Uncaught TypeError: Cannot read properties of null (reading 'getBoundingClientRect')

function isInViewport(el) {rect = el.getBoundingClientRect();

Can someone explain why this code does nothing except produce a console error?

verlager
  • 794
  • 5
  • 25
  • 43
  • 1
    `document.querySelector('#cap')` is returning null. This element is not in the DOM – Justin Taddei Aug 30 '22 at 20:34
  • Please provide your html – Justin Taddei Aug 30 '22 at 20:35
  • 1
    Does this answer your question? [Why does jQuery or a DOM method such as getElementById not find the element?](https://stackoverflow.com/questions/14028959/why-does-jquery-or-a-dom-method-such-as-getelementbyid-not-find-the-element) – Heretic Monkey Aug 30 '22 at 20:38
  • What I'm doing is: I have a table of 300 rows. I want to click on body outside of table and if is visable, scroll to last line, else scroll to "#cap" – verlager Aug 30 '22 at 20:46

3 Answers3

1

The element is not present in the DOM, check that the DOM is in a ready state before adding the click event:

function isInViewport(el) {
  rect = el.getBoundingClientRect();
  return (rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight ||
    document.documentElement.clientHeight) && rect.right <= (window.innerWidth ||
    document.documentElement.clientWidth));
}

document.addEventListener('DOMContentLoaded', function() {
    document.addEventListener('click', function() {
        box = document.querySelector('#cap');
        if (isInViewport(box)) {
            alert('true');
        } else {
            alert('false');
        }
    });
});

function isInViewport(el) {
  rect = el.getBoundingClientRect();
  return (rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight ||
    document.documentElement.clientHeight) && rect.right <= (window.innerWidth ||
    document.documentElement.clientWidth));
}

document.addEventListener('DOMContentLoaded', function() {
    document.addEventListener('click', function() {
        box = document.querySelector('#cap');
        if (isInViewport(box)) {
            alert('true');
        } else {
            alert('false');
        }
    });
});
<div id="cap"></div>
Andrés Andrade
  • 2,213
  • 2
  • 18
  • 23
  • Your code works, but too extensively. I want to target just the blank areas outside of all other divs (body blank areas) which your code doesn't do. Can you please fix it? – verlager Aug 30 '22 at 21:52
1

You can use the IntersectionObserver which tells you if the element is in the view. You can use the target to determine where the click happened.

let isIntersecting;

var observer = new IntersectionObserver(onIntersection, {
  root: null,
  threshold: .5,
})

function onIntersection(entries, opts) {
  isIntersecting = entries[0].isIntersecting;
}

observer.observe(document.querySelector('#thead'));

window.addEventListener("click", function (e) {
  if (e.target.closest("table")) return;
  console.log('Table not clicked and the header is', isIntersecting ? "visible" : "not visible");
});
table tr td {
  border: 1px solid black;
}
<table>
  <thead id="thead">
    <tr>
      <td>Header</td>
    </tr>
  </thead>
  <tbody>
    <tr><td>1</td></tr>
    <tr><td>2</td></tr>
    <tr><td>3</td></tr>
    <tr><td>4</td></tr>
    <tr><td>5</td></tr>
    <tr><td>6</td></tr>
    <tr><td>7</td></tr>
    <tr><td>8</td></tr>
    <tr><td>9</td></tr>
    <tr><td>10</td></tr>
    <tr><td>11</td></tr>
    <tr><td>12</td></tr>
    <tr><td>13</td></tr>
    <tr><td>14</td></tr>
    <tr><td>15</td></tr>
    <tr><td>16</td></tr>
    <tr><td>17</td></tr>
    <tr><td>18</td></tr>
    <tr><td>19</td></tr>
    <tr><td>20</td></tr>
    <tr><td>21</td></tr>
    <tr><td>22</td></tr>
    <tr><td>23</td></tr>
    <tr><td>24</td></tr>
    <tr><td>25</td></tr>
    <tr><td>26</td></tr>
    <tr><td>27</td></tr>
    <tr><td>28</td></tr>
    <tr><td>29</td></tr>    
  </tbody>  
</table>
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • This looks like great code, or close to it. But the chrome console says: Uncaught TypeError: Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'. – verlager Aug 31 '22 at 15:03
  • So did you change the id to match your id? – epascarello Aug 31 '22 at 17:46
0

My original code was correct and sufficient. I simply created and targeted two large clickable divs with absolute position. Rather than listening for a body click.

<div id = 'left_div' onclick = 'ring ();'></div>
<div id = 'right_riv onclick = 'ring ();'></div>

function isInViewport(el) {
 rect = el.getBoundingClientRect();
 return (rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight ||
 document.documentElement.clientHeight) && rect.right <= (window.innerWidth ||
 document.documentElement.clientWidth));
}

function ring() {
box = document.querySelector('#cap');
document.addEventListener('click', function() {
if (isInViewport(box)) {stone();} else {rock();}
});}
verlager
  • 794
  • 5
  • 25
  • 43