Azu
Azu

Reputation: 1550

An interesting Javascript task

enter image description here

JSFiddle

There has been a similar task recently and I tried to solve it by Javascript but couldn't. So I need your help.

The idea is this: it's like beads on a lace but the left and the right beads have blue lines only on one side. Also the last square. The structure should be responsive, that's why I have used flex.

The blue lines are spans with a background color, but could be also borders if you like. By the Javascript I move them to the left when I need to hide them. If you put a border to those divs .container > div, you'll get my idea.

I thought my Javacsript should work but actually it works only partly. Where am I wrong? Thank you.

const moveBackgrounds = () => {
    
    const divs = document.querySelectorAll('.container div');

    const getY = (elem) => {
        const elemRect = elem.getBoundingClientRect();
        return elemRect.top + window.scrollY;
    }
    
    let countPerLine = 0; //count of divs per line
    let firstDivY = getY(divs[0]);
    
    divs.forEach(div => {
        if (getY(div) === firstDivY ) {
            countPerLine++  
        }
    });

    //positioning the blue divs:
    divs.forEach((div, index) => {
        
        if ((index+1) % countPerLine !== 0) {
            const spanBg = div.querySelector('.bg');
            spanBg.style.left = 0 + 'px';
            
        }
        else {
            const spanBg = div.querySelector('.bg');
            const left = spanBg.style.left;
            spanBg.style.left = left - 20 + 'px';
        }
    });
    
    
    
    //move the background of the last div:
    const spanBg = divs[divs.length - 1].querySelector('.bg');
    
    console.log(spanBg);
    const left = spanBg.style.left;
    spanBg.style.left = left - 20 + 'px';
        
    
}

window.addEventListener("resize", moveBackgrounds);
window.addEventListener("load", moveBackgrounds);
.wrapper {
  text-align:center
}

.container {
  display: flex;
  flex-wrap: wrap;
  width: 40%;
  margin: 20px auto;

}
.container > div {
  width: 70px;
  height:70px;
  position:relative
}
.container > div .icon {
    display:inline-block;
    width: 50px;
    height: 50px;
    background-color: grey;
    position: absolute;
    left:0;
    top: 50%;
    transform:translate(0,-50%);
    z-index:2
}



.container .bg{
    display: block;
    width: 70px;
    height:2px;
    background-color: blue;
    position:absolute;
    z-index:1;
    top: 50%;
    left:0;
    transform:translateY(-50%)
}
<div class="wrapper">
    <div class="container">
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
    </div>
</div>

Upvotes: 0

Views: 161

Answers (3)

Alohci
Alohci

Reputation: 82976

This effect can be achieved entirely in CSS. To do so, the blue lines are created as the border or background of a space character, and makes use of the fact that spaces at the end of lines are dropped.

.container > div {
  display: inline;
}
.container > div .icon {
  display: inline-block;
  width: 50px;
  height: 50px;
  background-color: grey;
}
.container .bg::before {
  content: ' ';
  letter-spacing: 20px; /* adjust this to control the blue line length */
  font-size: 25px;
  border-bottom: 2px solid blue;
  vertical-align: top;
}

/* Just for demonstrating the responsiveness */
.container {
  border: 1px solid;
  animation: 10s linear 1s infinite alternate setWidth;
}
@keyframes setWidth { from { width: 100%; } to { width: 30%; }  }
    <div class="wrapper">
      <div class="container">
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
        <div><span class="icon"></span><span class="bg"></span></div>
      </div>
    </div>

Upvotes: 1

A Haworth
A Haworth

Reputation: 36426

The problem lies with the calculation of the moving of a blue line element.

Notice that on first load the layout looks fine - the right hand blue line is not there - but on a resize of the window they come back.

Here's what's happening. This line

const left = spanBg.style.left;

picks up the style, which at the start is empty as it hasn't been set. However, once it has been set (either to 0px or -20px) then that's the string that is returned. Notice it is not a number, it has px at the end. Then you try to take away 20 and add a px string. This isn't recognised so the style.left stays as it was - either 0px or 20px.

This snippet sets the blue lines to display: none if they are on the end rather than trying to hide them by moving them. It also has to then set them back to display: block when they are not to be removed (just in case they'd been set to none the last time).

const moveBackgrounds = () => {
  const divs = document.querySelectorAll('.container div');

  const getY = (elem) => {
    const elemRect = elem.getBoundingClientRect();
    return elemRect.top + window.scrollY;
  }

  let countPerLine = 0; //count of divs per line
  let firstDivY = getY(divs[0]);

  divs.forEach(div => {
    if (getY(div) === firstDivY) {
      countPerLine++
    }
  });

  //positioning the blue divs:
  divs.forEach((div, index) => {

    if ((index + 1) % countPerLine !== 0) {
      const spanBg = div.querySelector('.bg');
      spanBg.style.display = 'block';

    } else {
      const spanBg = div.querySelector('.bg');
      spanBg.style.display = 'none';
    }
  });



  //move the background of the last div:
  const spanBg = divs[divs.length - 1].querySelector('.bg');

  //console.log(spanBg);
  const left = spanBg.style.left;
  //spanBg.style.left = left - 20 + 'px';
  spanBg.style.display = 'none';

}

window.addEventListener("resize", moveBackgrounds);
window.addEventListener("load", moveBackgrounds);
.wrapper {
  text-align: center
}

.container {
  display: flex;
  flex-wrap: wrap;
  width: 40%;
  margin: 20px auto;
}

.container>div {
  width: 70px;
  height: 70px;
  position: relative
}

.container>div .icon {
  display: inline-block;
  width: 50px;
  height: 50px;
  background-color: grey;
  position: absolute;
  left: 0;
  top: 50%;
  transform: translate(0, -50%);
  z-index: 2
}

.container .bg {
  display: block;
  width: 70px;
  height: 2px;
  background-color: blue;
  position: absolute;
  z-index: 1;
  top: 50%;
  left: 0;
  transform: translateY(-50%)
}
<div class="wrapper">
  <div class="container">
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
    <div><span class="icon"></span><span class="bg"></span></div>
  </div>
</div>

Upvotes: 1

Thaeke Hekkenberg
Thaeke Hekkenberg

Reputation: 378

As far as I can see you will get what you want by using:

spanBg.style.display = 'none'

for the last element in the divs list.

If I'm wrong, let me know.

Image added so you can see the result.

enter image description here

Upvotes: 1

Related Questions