Tian Qin
Tian Qin

Reputation: 183

HTML/CSS Efficient way to style duplicated elements?

I am trying to make a ball shooting game use only vanilla Javascript for fun.

I want to have 10 or much more circles moving randomly on my screen. In below code, I have 10 circles list on the left side of my screen, they all have same color and size, only the positions are different. This is my HTML:

body {
  width: 100vw;
  height: 100vh;
}
.circle {
/*
  position: absolute;
  top: 1px;
*/
  width: 50px;
  height: 50px;
  color: transparent;
  border: 2px solid red;
  border-radius: 50%;
}

.circle:nth-child(1) {
position: absolute;
top: 1px;
}

.circle:nth-child(2) {
position: absolute;
top: 60px;
}

.circle:nth-child(3) {
position: absolute;
top: 120px;
}

.circle:nth-child(4) {
position: absolute;
top: 180px;
}

.circle:nth-child(5) {
position: absolute;
top: 240px;
}

.circle:nth-child(6) {
position: absolute;
top: 300px;
}

.circle:nth-child(7) {
position: absolute;
top: 360px;
}

.circle:nth-child(8) {
position: absolute;
top: 420px;
}

.circle:nth-child(9) {
position: absolute;
top: 480px;
}

.circle:nth-child(10) {
position: absolute;
top: 540px;
}
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>


    

I am new to Front-end tech. Is there any efficient way to style the circles better than this? If I like to have 100 circles on the screen I don't want to create 100 classes and style them one by one...

Thanks

Upvotes: 0

Views: 98

Answers (2)

David Thomas
David Thomas

Reputation: 253308

The easiest answer is, looking at your code, to use JavaScript's NodeList.prototype.forEach():

// using document.querySelectorAll() to find all elements
// matching the supplied CSS selector, and then
// NodeList.prototype.forEach() to iterate over each Node
// of that NodeList:
document.querySelectorAll('.circle').forEach(

    // using an Arrow function expression;
    // 'circle': a reference to the current Node of the NodeList,
    // 'index': the index of the current Node in the NodeList:
    // here we set the circle.style.top property to be equal to
    // the result of 60 * index concatenated with the 'px' unit:
    (circle, index) => circle.style.top = 60 * index + 'px'
);

document.querySelectorAll('.circle').forEach(
    (circle, index) => circle.style.top = 60 * index + 'px'
);
body {
  width: 100vw;
  height: 100vh;
}
.circle {
  position: absolute;
  width: 50px;
  height: 50px;
  color: transparent;
  border: 2px solid red;
  border-radius: 50%;
}
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>

Admittedly this does place the first .circle element with its top: 0px rather than 1px, so it's entirely possible to revise the above approach to explicitly style the .circle elements with top: 1px and then use the JavaScript to style all .circle elements except the first:

document.querySelectorAll('.circle + .circle').forEach(
    (circle, index) => circle.style.top = 60 + (60 * index) + 'px'
);

document.querySelectorAll('.circle + .circle').forEach(
    (circle, index) => circle.style.top = 60 + (60 * index) + 'px'
);
body {
  width: 100vw;
  height: 100vh;
}
.circle {
  position: absolute;
  top: 1px;
  width: 50px;
  height: 50px;
  color: transparent;
  border: 2px solid red;
  border-radius: 50%;
}
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>

Further to the above, using a templating language such as Vue (this still involves JavaScript):

// for something so simple Vue is - almost certainly -
// overkill, however: Vue is initialised (in this very
// simple case) as follows:
new Vue({

  // 'el' takes a CSS selector to identify the
  // elements in, or upon, which Vue should run:
  'el': '.wrapper'
});
*,
 ::before,
 ::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.circle {
  position: absolute;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: 2px solid #f00;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
<div class="wrapper">
  <!--
    here we define the element that will be repeated,
    to repeat the element we use the v-for attribute,
    here we use the 'i in 10' (i is the current number,
    starting at 1, 10 is the end of the range); to adjust
    the 'style' attribute, we preface the attribute with
    the ':' character, and within that attribute we use:
      `top: ${i * 60 - 60)px`
    this is a JavaScript template literal syntax, in which
    the 'i * 60 - 60' wrapped by '${...}' is interpolated
    by Vue to generate the relevant values
  -->
  <div class="circle" v-for="i in 10" :style="`top: ${i * 60 - 60}px`"></div>
</div>

Upvotes: 1

sadrzadehsina
sadrzadehsina

Reputation: 1349

It would be better if you could create/append your balls to the body via javascript. Something like below.

var ball = document.createElement('div');
ball.className = 'circle';

document.body.appendChild(ball);

Then you can change each ball position randomly like below.

ball.style.position.top = <your_random_number>;

It would also better if you put position: absolute in base .circle css selector.

Above code snippet is for one ball. You can simple extend it for more than one by justing pushing each created ball in array and then iterate over the array and append each one to the body.

Upvotes: 0

Related Questions