0

I'm trying to create a progress circle, and get the progress value based on HTML data (data-start and data progress), but there is an error.

The code works fine on one element, but the problem occurs when creating a loop for all elements.

Uncaught Type Error: Cannot read properties of undefined (reading 'start')"

let progressBar = document.getElementsByClassName(".circular-progress");
let valueContainer = document.getElementsByClassName(".value-container");


let progressValue = valueContainer.dataset.start;
let progressEndValue = valueContainer.dataset.progress;





var twoSum = function() {

  for (var i = 0; i < progressValue.length; i++) {
    for (var j = 0; j < progressEndValue.length; j++) {

      let progress = setInterval(() => {
        i++;
        valueContainer.textContent = `${[i]}%`;
        progressBar.style.background = `conic-gradient(
                    #4d5bf9 ${[i] * 3.6}deg,
                    #cadcff ${[i] * 3.6}deg
                )`;
        if (progressValue == progressEndValue) {
          clearInterval(progress);
        }
      }, 30);
    }
  }
};
.skill .container {
  display: grid;
  align-items: center;
}

.skill-grid {
  display: grid;
  grid-template-columns: 190px 190px;
  grid-template-rows: 190px 190px;
  justify-content: center;
  column-gap: 2.5rem;
  row-gap: 100px;
  margin-right: 2rem;
  position: relative;
}

.skill-card {
  background-color: var(--white);
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: 1 1 6rem;
  /* padding: 3rem 4rem; */
  border-radius: 0.5rem;
  transition: transform ease-in-out 0.3s;
}

.skill-card p {
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  padding-top: 10px;
}

.circular-progress {
  position: relative;
  height: 100px;
  width: 100px;
  border-radius: 50%;
  display: grid;
  place-items: center;
}

.circular-progress:before {
  content: "";
  position: absolute;
  height: 90%;
  width: 90%;
  background-color: #ffffff;
  border-radius: 50%;
}

.value-container {
  position: relative;
  font-family: var(--main-font);
  font-size: 1.3rem;
  color: var(--bs-primary);
  font-weight: 600;
}
<section class="skill">
  <div class="container">
    <div class="skill-grid">
      <div class="skill-card">
        <div class="circular-progress">
          <div class="value-container" data-start="0" data-progress="90">0</div>
        </div>
        <p>UI Design</p>
      </div>
      <div class="skill-card">
        <div class="circular-progress">
          <div class="value-container" data-start="0" data-progress="80">0</div>
        </div>
        <p>UI Design</p>
      </div>
      <div class="skill-card">
        <div class="circular-progress">
          <div class="value-container" data-start="0" data-progress="60">0%</div>
        </div>
        <p>UI Design</p>
      </div>
      <div class="skill-card">
        <div class="circular-progress">
          <div class="value-container" data-start="0" data-progress="50">0%</div>
        </div>
        <p>UI Design</p>
      </div>
    </div>


  </div>
</section>
Michael M.
  • 10,486
  • 9
  • 18
  • 34
  • 2
    The result of`getElementsByClassName` is a NodeList which is a **collection** of elements. Note how the function name says "getElement**s**". This collection does not have a `dataset`, only the items inside it do. For more info, see [How to correctly iterate through getElementsByClassName](https://stackoverflow.com/q/15843581/1220550) – Peter B Jan 14 '23 at 18:16
  • Remove Dot in the CSS Class name Change to let progressBar = document.getElementsByClassName("circular-progress"); let valueContainer = document.getElementsByClassName("value-container"); – C Tech Hindi Jan 14 '23 at 18:34
  • Check this https://jsbin.com/hihururaji/1/edit?js,console,output – C Tech Hindi Jan 14 '23 at 18:54

1 Answers1

0

You can easily simplify your code:

Better use the querySelectorAll() (and it's single element counterpart querySelector() selection method, since it provides a more versatile css-like selector syntax.

As commented by @Peter B getElementsByClassName()
selects multiple elements (besides it's expect the raw class name without trailing "." dots)
thus, you need to loop through all elements and process each item.

// find all progress circles
let progressBars = document.querySelectorAll(".circular-progress");
progressBars.forEach((progressBar) => {
  // select child value container
  let valueContainer = progressBar.querySelector(".value-container");
  let progressValue = valueContainer.dataset.start;
  let progressEndValue = valueContainer.dataset.progress;
  let i = 0;
  // increment progress angles
  let progress = setInterval(() => {
    i++;
    valueContainer.textContent = `${[i]}%`;
    progressBar.style.background = `conic-gradient(
                    #4d5bf9 ${i * 3.6}deg,
                    #cadcff ${i * 3.6}deg
                )`;
  // stop animation
    if (i >= progressEndValue) {
      clearInterval(progress);
    }
  }, 10);
});
.skill .container {
    display: grid;
    align-items: center;
}

.skill-grid {
    display: grid;
    grid-template-columns: 190px 190px;
    grid-template-rows: 190px 190px;
    justify-content: center;
    column-gap: 2.5rem;
    row-gap: 100px;
    margin-right: 2rem;
    position: relative;
}
.skill-card {
    background-color: var(--white);
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;

  flex: 1 1 6rem;
    /* padding: 3rem 4rem; */
    border-radius: 0.5rem;

  
   transition:  transform ease-in-out 0.3s;
}

.skill-card p {
    font-size: 1rem;
    font-weight: 600;
    text-align: center;
    padding-top: 10px;
}

.circular-progress {
    position: relative;
    height: 100px;
    width: 100px;
    border-radius: 50%;
    display: grid;
    place-items: center;
  }
  .circular-progress:before {
    content: "";
    position: absolute;
    height: 90%;
    width: 90%;
    background-color: #ffffff;
    border-radius: 50%;
  }
  .value-container {
    position: relative;
    font-family: var(--main-font);
    font-size: 1.3rem;
    color: var(--bs-primary);
    font-weight: 600;
   }
<section class="skill">
    <div class="container">
        <div class="skill-grid">
            <div class="skill-card">
                <div class="circular-progress">
                    <div class="value-container" data-start="0" data-progress="90">0</div>
                  </div>
                <p>UI Design</p>
            </div>
            <div class="skill-card">
                <div class="circular-progress">
                    <div class="value-container" data-start="0" data-progress="80">0</div>
                  </div>
                <p>UI Design</p>
            </div>
          <div class="skill-card">
                <div class="circular-progress">
                    <div class="value-container" data-start="0" data-progress="60">0%</div>
                  </div>
                <p>UI Design</p>
            </div>
            <div class="skill-card">
                <div class="circular-progress">
                    <div class="value-container" data-start="0" data-progress="50">0%</div>
                  </div>
                <p>UI Design</p>
            </div>
        </div>
    </div>
</section>

In the above example we're selecting the outer progress circle elements.
Since the text labels are just child elements, we can select them within the loop.

herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
  • "Thank you. it workes now with me. i also added a feature to scroll to the start of the work. However, there is a defect in it. The program runs continuously without stopping." – mahmoud refat Feb 27 '23 at 19:15
  • Probably you have some scroll event handler or intersection observer callling the function over and over again. Please add your current code (containing the infinite loop issue). – herrstrietzel Feb 27 '23 at 19:50
  • When I reach the section, it starts working, but if i goes up or down, it starts working again > the code https://codepen.io/rf3t/pen/vYxPeKz – mahmoud refat Mar 03 '23 at 08:49
  • Pardon me but that's really another question. To give you some hints: better use an [`IntersectionObserver()`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) – The native `IntersectionObserver` method was created for this task to replace on-scroll event handlers - infamous for slowing down your rendering (since they call a function many times while scrolling). It's also crucial to understand you always need some condition to stop processing – e.g by setting a "completed" class. Here's [an example](https://codepen.io/herrstrietzel/pen/OJomdwN). – herrstrietzel Mar 04 '23 at 00:02