2

I'm trying to draw a line between the clicked element and '#identifier' using element.getBoundingClientRect(). element.getBoundingClientRect() is not getting the actual coordinates of element due to some other elements on the page which are positioned relative. When I tried to comment out the '#header' and '#greytext' elements, the line is displaying as expected. Will the other elements on the page effect the getBoundingClientRect() coordinates?

Here is my code:
Misaligned lines: https://jsfiddle.net/SampathPerOxide/jdyoz3r4/34/
Aligned lines after commenting out the '#header' and '#greytext': https://jsfiddle.net/SampathPerOxide/kqfjvs04/7/

Can someone clarify why the '#header' and '#greytext' elements are disturbing the '#identifier' coordinates?

<style>
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap');

html,
body {
  font-family: 'Ubuntu', sans-serif;
  background-color: lightblue;
  color: #000;
}

#content {
  color: #787878;
}

.container-fluid-child {
  background-color: #fff;
  position: relative;
  padding: 5%;
}

.container-fluid-innerchild {
  background-color: #fff;
  position: relative;
  border-radius: 25px;
}

.text-white {
  color: #fff;
}

.car-image {
  width: 35%;
  max-width: 35%;
  border: 1px solid #000;
}

.step-one {
  width: 130px;
  height: 130px;
  background-color: #81a1b6;
  color: #fff;
  justify-content: center;
  align-items: center;
  display: flex;
  border-radius: 50%;
}

.sound-icon {
  width: 20px;
  float: left;
  margin-right: 10px;
}

.set-sound {
  position: absolute;
  top: 2.4em;
  width: 100%;
}

.intro-para {
  position: absolute;
  width: 35%;
  font-size: 1.2em;
}

.hyperlink {
  color: #fff;
  text-decoration: none;
  font-size: 1.3em;
}

.hyperlink:hover {
  color: #fff;
  font-weight: 900;
  text-decoration: none;
}

.hyperlink:focus {
  color: #fff;
  text-decoration: none;
}

.footer {
  padding: 1em;
}

.text-over-image {
  float: left;
  position: absolute;
  bottom: 23em;
  left: 5em;
}

.relative {
  position: relative;
}

.textonimage {
  position: absolute;
  max-width: 30%;
  top: 2%;
  font-weight: 600;
}

.ableft {
  left: 1em;
}

.abright {
  right: 1em;
}

.car {
  cursor: pointer;
}

.grey {
  color: grey;
  font-weight: 600;
  margin-left: 1.5em;
  max-width: 50%;
  position: relative;
  top: 1em;
}

.no-padding {
  padding: 0px;
}

.firstcar-detail {
  border-radius: 25px;
}

#hr {
  -moz-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  transform: rotate(-45deg);
  top: 12%;
  left: 18%;
  height: 20%;
  background-color: #000;
  width: 1px;
  position: absolute;

}

#hrtext {
  position: absolute;
  bottom: 85%;
  left: 5%;
}

.series-ul li {
  list-style: none;
  color: grey;
  font-size: 1em;
  font-weight: 600;
  border: 2px solid grey;
  display: table;
  padding: 0.3em 0.1em;
  text-align: center;
  margin: 0.5em;
  cursor: pointer;
}

.series-div {
  position: absolute;
  left: 65%;
  top: 0em;
}

.identifier {
  width: 10px;
  height: 10px;
  background-color: red;
  position: absolute;
  right: 45%;
  top: 30%;
}

#line {
  position: absolute;
  width: 2px;
  margin-top: -1px;
  background-color: red;
}

.fullwidth-img {
  width: 100%;
}

.width70-img {
  width: 90%;
  margin: 0px auto;
  margin-top: 10%;
}

.margintop10 {
  margin-top: 10%;
}

.hide {
  display: none;
}

.pointer {
  cursor: pointer;
}

.bottom-text {
  position: absolute;
  bottom: 5%;
  left: 5%;
  max-width: 15%;
}

@media only screen and (max-width: 1024px) {
  .series-ul li {
    font-size: 1em;
    min-width: auto;
  }

  .grey {
    max-width: 100%;
    font-size: 1.5em;
    top: auto;
  }

  .header .text-right {
    text-align: center;
    font-size: 2em;
  }

  .set-sound {
    top: 4em;
  }

  .bottom-text {
    bottom: 0%;
    max-width: 100%;
  }

  .intro-para {
    position: relative;
    width: 100%;
    text-align: center;
  }

  .textonimage {
    bottom: 100%;
    top: auto;
  }
}

</style>
    <div class="container-fluid">
        
        <div id="header"><div class="row  header">
        <div class="col-md-12 col-sm-12 col-xs-12">
            <div class="col-md-8 col-sm-8 col-xs-8"> <h1 class="text-right">Some dummy placeholder - Random text </h1></div>
            <div class="col-md-4 col-sm-4 col-xs-4">
                <div class="set-sound">
                    <img src="images/icons8-sound-50.png" class="img-responsive sound-icon hide">
    
            <p>Some dummy placeholder</p>
                </div>
            </div>
        </div>
    </div>
    </div>
        <div id="content"><div class="container-fluid-innerchild">
           <div class="row">
    <div class="col-md-12 col-sm-12 col-xs-12">
        <div class="col-md-7 col-sm-7 col-xs-7 no-padding">
            <div id="greytext">
                <h2 class="grey">Some dummy placeholder - Random text</h2>
            </div>
            <br>
         <div class="relative">
        <div >
            <div id="hrtext"><p>Some dummy placeholder
        </p></div>
        
        <div id="hr"></div>
    </div>
                <div class="identifier" id="identifier"></div>
                <div class="series-div">
                    <ul class="series-ul">
                      <li class="seriesli">A 1</li>
                
                      <li class="seriesli">B 1</li>
                  
                      <li class="seriesli">C 1</li>
                    
                    </ul>
                  </div>
                <div id="line"></div>
                       <img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive firstcar-detail fullwidth-img">
               
        </div>
       
                   </div>
        
        <div class="col-md-5 col-xs-5 col-sm-5">
            
    <div>
        <img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive width70-img firstgraph pointer">
        <img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive width70-img zoomedgraph">
    </div>
              
        </div>
        
           </div>
           
        </div>
        </div></div>
        <div id="footer"><div class="row text-white footer">
        <div class="col-md-12 col-sm-12 col-xs-12">
            <div class="col-md-2 col-sm-2 col-xs-2"><a href="javascript:void(0);" class="hyperlink">Home</a>            </div>
            <div class="col-md-10 col-sm-10 col-xs-10 text-right"><a href="#" class="hyperlink">google.com</a></div>
        </div>
    </div>
    </div>
        
    </div>
<script>
$('.seriesli').click(function() {

  function adjustLine(from, to, line) {

    // Get actual position relative to viewport.
    // See https://stackoverflow.com/a/11396681/117030
    fromBCR = from.getBoundingClientRect();
    toBCR   = to.getBoundingClientRect();
    
    var fT = fromBCR.top + from.offsetHeight / 2;
    var tT = toBCR.top + to.offsetHeight / 2;
    
    // Don't add offsetWidth. This connects to the middle, not the left edge.
    var fL = fromBCR.left //+ from.offsetWidth / 2;
    var tL = toBCR.left + to.offsetWidth / 2;

    var CA = Math.abs(tT - fT);
    var CO = Math.abs(tL - fL);
    var H = Math.sqrt(CA * CA + CO * CO);
    var ANG = 180 / Math.PI * Math.acos(CA / H);

    if (tT > fT) {
      var top = (tT - fT) / 2 + fT;
    } else {
      var top = (fT - tT) / 2 + tT;
    }
    if (tL > fL) {
      var left = (tL - fL) / 2 + fL;
    } else {
      var left = (fL - tL) / 2 + tL;
    }

    if ((fT < tT && fL < tL) || (tT < fT && tL < fL) || (fT > tT && fL > tL) || (tT > fT && tL > fL)) {
      ANG *= -1;
    }
    top -= H / 2;

    line.style["-webkit-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-moz-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-ms-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-o-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-transform"] = 'rotate(' + ANG + 'deg)';
    line.style.top = top + 'px';
    line.style.left = left + 'px';
    line.style.height = H + 'px';
  }
  adjustLine(
    this, // Element that was clicked.
    document.getElementById('identifier'),
    document.getElementById('line')
  );
});

</script>
Sampath
  • 153
  • 2
  • 13
  • `getBoundingClientRect()` works absolutely fine. The problem with your code is that the calculated coordinates are relative to the page's top left corner, but your line div, while correctly positioned absolutely, isn't positioned relative to the page's top left corner but some other divs (this is how `position: absolute` works). If you move the line div to the very end of your HTML code, it'll work fine. –  Jul 22 '21 at 06:50
  • Moving the line div to the bottom of the page works fine, but as @Constantin Groß said we need to add window.scrollX values and window.scrollY values. Thanks for your comments. – Sampath Jul 22 '21 at 07:13

2 Answers2

2

As suggested in the comments, your #line element has to be moved to the document root. (It doesn't really matter if at the beginning or at the end, but if added at the top, you'd also have to set z-index to a value higher than any elements otherwise overlaying it)

In addition to that, you'll have to take the window's scroll position into account, so add window.scrollX to the .left values and window.scrollY to the .top values.

$('.seriesli').click(function() {

  function adjustLine(from, to, line) {

    // Get actual position relative to viewport.
    // See https://stackoverflow.com/a/11396681/117030
    fromBCR = from.getBoundingClientRect();
    toBCR = to.getBoundingClientRect();

    var fT = fromBCR.top + from.offsetHeight / 2 + window.scrollY;
    var tT = toBCR.top + to.offsetHeight / 2 + window.scrollY;

    // Don't add offsetWidth. This connects to the middle, not the left edge.
    var fL = fromBCR.left + window.scrollX //+ from.offsetWidth / 2;
    var tL = toBCR.left + to.offsetWidth / 2 + window.scrollX;

    var CA = Math.abs(tT - fT);
    var CO = Math.abs(tL - fL);
    var H = Math.sqrt(CA * CA + CO * CO);
    var ANG = 180 / Math.PI * Math.acos(CA / H);

    if (tT > fT) {
      var top = (tT - fT) / 2 + fT;
    } else {
      var top = (fT - tT) / 2 + tT;
    }
    if (tL > fL) {
      var left = (tL - fL) / 2 + fL;
    } else {
      var left = (fL - tL) / 2 + tL;
    }

    if ((fT < tT && fL < tL) || (tT < fT && tL < fL) || (fT > tT && fL > tL) || (tT > fT && tL > fL)) {
      ANG *= -1;
    }
    top -= H / 2;

    line.style["-webkit-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-moz-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-ms-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-o-transform"] = 'rotate(' + ANG + 'deg)';
    line.style["-transform"] = 'rotate(' + ANG + 'deg)';
    line.style.top = top + 'px';
    line.style.left = left + 'px';
    line.style.height = H + 'px';
  }
  adjustLine(
    this, // Element that was clicked.
    document.getElementById('div2'),
    document.getElementById('line')
  );
});
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap');
html,
body {
  font-family: 'Ubuntu', sans-serif;
  background-color: lightblue;
  color: #000;
}

#content {
  color: #787878;
}

.container-fluid-child {
  background-color: #fff;
  position: relative;
  padding: 5%;
}

.container-fluid-innerchild {
  background-color: #fff;
  position: relative;
  border-radius: 25px;
}

.text-white {
  color: #fff;
}

.car-image {
  width: 35%;
  max-width: 35%;
  border: 1px solid #000;
}

.step-one {
  width: 130px;
  height: 130px;
  background-color: #81a1b6;
  color: #fff;
  justify-content: center;
  align-items: center;
  display: flex;
  border-radius: 50%;
}

.sound-icon {
  width: 20px;
  float: left;
  margin-right: 10px;
}

.set-sound {
  position: absolute;
  top: 2.4em;
  width: 100%;
}

.intro-para {
  position: absolute;
  width: 35%;
  font-size: 1.2em;
}

.hyperlink {
  color: #fff;
  text-decoration: none;
  font-size: 1.3em;
}

.hyperlink:hover {
  color: #fff;
  font-weight: 900;
  text-decoration: none;
}

.hyperlink:focus {
  color: #fff;
  text-decoration: none;
}

.footer {
  padding: 1em;
}

.text-over-image {
  float: left;
  position: absolute;
  bottom: 23em;
  left: 5em;
}

.relative {
  position: relative;
}

.textonimage {
  position: absolute;
  max-width: 30%;
  top: 2%;
  font-weight: 600;
}

.ableft {
  left: 1em;
}

.abright {
  right: 1em;
}

.car {
  cursor: pointer;
}

.grey {
  color: grey;
  font-weight: 600;
  margin-left: 1.5em;
  max-width: 50%;
  position: relative;
  top: 1em;
}

.no-padding {
  padding: 0px;
}

.firstcar-detail {
  border-radius: 25px;
}

#hr {
  -moz-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  transform: rotate(-45deg);
  top: 12%;
  left: 18%;
  height: 20%;
  background-color: #000;
  width: 1px;
  position: absolute;
}

#hrtext {
  position: absolute;
  bottom: 85%;
  left: 5%;
}

.series-ul li {
  list-style: none;
  color: grey;
  font-size: 1em;
  font-weight: 600;
  border: 2px solid grey;
  display: table;
  padding: 0.3em 0.1em;
  text-align: center;
  margin: 0.5em;
  cursor: pointer;
}

.series-div {
  position: absolute;
  left: 65%;
  top: 0em;
}

.identifier {
  width: 10px;
  height: 10px;
  background-color: red;
  position: absolute;
  right: 45%;
  top: 30%;
}

#line {
  position: absolute;
  width: 2px;
  margin-top: -1px;
  background-color: red;
}

.fullwidth-img {
  width: 100%;
}

.width70-img {
  width: 90%;
  margin: 0px auto;
  margin-top: 10%;
}

.margintop10 {
  margin-top: 10%;
}

.hide {
  display: none;
}

.pointer {
  cursor: pointer;
}

.bottom-text {
  position: absolute;
  bottom: 5%;
  left: 5%;
  max-width: 15%;
}

@media only screen and (max-width: 1024px) {
  .series-ul li {
    font-size: 1em;
    min-width: auto;
  }
  .grey {
    max-width: 100%;
    font-size: 1.5em;
    top: auto;
  }
  .header .text-right {
    text-align: center;
    font-size: 2em;
  }
  .set-sound {
    top: 4em;
  }
  .bottom-text {
    bottom: 0%;
    max-width: 100%;
  }
  .intro-para {
    position: relative;
    width: 100%;
    text-align: center;
  }
  .textonimage {
    bottom: 100%;
    top: auto;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container-fluid">

  <div id="header">
    <div class="row  header">
      <div class="col-md-12 col-sm-12 col-xs-12">
        <div class="col-md-8 col-sm-8 col-xs-8">
          <h1 class="text-right">Some dummy placeholder - Random text </h1>
        </div>
        <div class="col-md-4 col-sm-4 col-xs-4">
          <div class="set-sound">
            <img src="images/icons8-sound-50.png" class="img-responsive sound-icon hide">

            <p>Some dummy placeholder</p>
          </div>
        </div>
      </div>
    </div>
  </div>
  <div id="content">
    <div class="container-fluid-innerchild">
      <div class="row">
        <div class="col-md-12 col-sm-12 col-xs-12">
          <div class="col-md-7 col-sm-7 col-xs-7 no-padding">
            <div id="greytext">
              <h2 class="grey">Some dummy placeholder - Random text</h2>
            </div>
            <br>
            <div class="relative">
              <div>
                <div id="hrtext">
                  <p>Some dummy placeholder
                  </p>
                </div>

                <div id="hr"></div>
              </div>
              <div class="identifier" id="div2"></div>
              <div class="series-div">
                <ul class="series-ul">
                  <li class="seriesli" id="div1">A 1</li>

                  <li class="seriesli">B 1</li>

                  <li class="seriesli">C 1</li>

                </ul>
              </div>
              <img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive firstcar-detail fullwidth-img">

            </div>

          </div>

          <div class="col-md-5 col-xs-5 col-sm-5">

            <div>
              <img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive width70-img firstgraph pointer">
              <img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive width70-img zoomedgraph">
            </div>

          </div>

        </div>

      </div>
    </div>
  </div>
  <div id="footer">
    <div class="row text-white footer">
      <div class="col-md-12 col-sm-12 col-xs-12">
        <div class="col-md-2 col-sm-2 col-xs-2"><a href="javascript:void(0);" class="hyperlink">Home</a> </div>
        <div class="col-md-10 col-sm-10 col-xs-10 text-right"><a href="#" class="hyperlink">google.com</a></div>
      </div>
    </div>
  </div>

</div>
<div id="line"></div>
Constantin Groß
  • 10,719
  • 4
  • 24
  • 50
  • Adding window.scrollX, window.scrollY values and then moving the line div to bottom of the page solved my issue. thank you. – Sampath Jul 22 '21 at 07:23
  • 1
    I think a better solution is to figure out the closest relative parent and subtract its coordinates: https://jsfiddle.net/khrismuc/cnh7L3u8/ –  Jul 22 '21 at 07:24
-1

So many js effort exist here, for my opinion of course.

Why don't you simply use CSS?

For example:

HTML

<ul class="series-ul">
  <li id="line1" class="active">A 1</li>
  <li id="line2">B 1</li>
  <li id="line3">C 1</li>
</ul>

CSS

.series-ul li {
  position: relative;
}

.series-ul li.active {
  background: red;
  color: white;
  border-color: red;
}

.series-ul li::after {
  position: absolute;
  content: " ";
  display: none;
  width: 150px;
  height: 5px;
  border-top: 1px solid red;
}

.series-ul li.active::after {
  display: block;
}

#line1::after {
  transform: rotate(-31deg);
  top: 46px;
  right: 16px;
  width: 120px;
}

#line2::after {
  transform: rotate(-15deg);
  top: 26px;
  right: 21px;
  width: 106px;
}

#line3::after {
  transform: rotate(8deg);
  top: 9px;
  right: 23px;
  width: 103px;
}

JS

$(".series-ul li").click(function() {
    $(".series-ul li").removeClass("active");
    $(this).addClass("active");
});

Demo: https://jsfiddle.net/caneroncel/fmq9p1Lt/15/

Caner
  • 92
  • 3
  • 1
    Probably because the now static demo might be a dynamic site later with different (maybe configurable) elements with different positions? – Constantin Groß Jul 22 '21 at 07:05
  • Still he can use media queries, instead of this effort. – Caner Jul 22 '21 at 07:08
  • 1
    It has nothing to do with media queries, but with a theoretically infinite number of ways that target objects and therefore lines might be arranged. – Constantin Groß Jul 22 '21 at 07:09
  • This is a easy and quick way, but not a reliable answer. We need to keep on adding the CSS and media queries every time we add a new li. Anyway, thanks for the answer. – Sampath Jul 22 '21 at 07:26