skip to Main Content

I would like to implement some animation for the hp-bar as it changes. For context, this is a Chrome extension for a browser game to display team information.

I had a previous version that worked fine, but I refactored the code so I could arrange the team member cards alphabetically. The width/color of the hp-bar is behaving as expected.

I have tried calling setHp() both before/after the appendChild elements, switching the transition property into JS .hp-container, including it in the function itself, and changing CSS from .hp-bar to #cardOneHP, #cardTwoHp, ....

I suspect that this has something to do with how JS is creating the elements and that’s interfering with a smooth transition, but I am not sure how to solve.

const hpBarGreen = "#39ff7b";
const hpBarYellow = "#f3b200";
const hpBarRed = "#fb3041";

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
      if (request.data) {
        const party = request.data;

        const members = [];

        function createMemberObject(partyMember, index) {
          if (!partyMember) return null;

          return {
            id: `member${index + 1}`,
            originalName: partyMember.name,
            sortName: partyMember.name.replace("G-Max ", "").replace("Mega ", ""),
            form: partyMember.form,
            level: partyMember.level,
            typeOne: partyMember.types[0],
            typeTwo: partyMember.types[1],
            teraType: partyMember.teraType,
            status: partyMember.status,
            currentHp: partyMember.currentHP,
            maxHp: partyMember.maxHP
          };
        }

        for (let i = 0; i <= 5; i++) {
          const memberObject = createMemberObject(party[i], i)
          if (memberObject) {
            members.push(memberObject);
          }
        };

        function displayMembers(members) {
          const popup = document.querySelector(".popup");
          popup.innerHTML = "";

        members.forEach(member => {
          const card = document.createElement('div');
          card.className = 'pokemon-card';
          card.id = member.id;

          card.innerHTML = `
            <div class="sprite">
                <img class="pokemon-sprite" id="${member.id}Sprite" src="./images/pokemon/${
                  member.form ? `${member.sortName}-${member.form}` : `${member.sortName}`
                }.png" alt="">
                <img id="${member.id}Form" src="" alt="">
            </div>
            <div class="stats">
                <div class="line-one">
                    <div class="name" id="${member.id}Name">${member.sortName.toUpperCase()}</div>
                    <div class="level-container">
                        <p id="${member.id}LevelText">Lv.</p>
                        <p class="level-number" id="${member.id}Level">${member.level}</p>
                    </div>
                </div>
                <div class="line-two">
                  <div class="types">
                      <img id="${member.id}TypeOne" src="${
                        member.teraType ? `./images/tera-types/${member.teraType}.png` : `./images/types/${member.typeOne}.png`
                      }" alt="">
                      <img id="${member.id}TypeTwo" src="${
                        member.teraType ? "" : `./images/types/${member.typeTwo}.png`
                      }" alt="">
                  </div>
                </div>
                <div class="line-three">
                    <img id="${member.id}Status" class="status" src="./images/status/${member.status}.png" alt="">
                </div>
                <div class="line-four">
                    <div id="${member.id}HpBar" class="hp-container">
                        <div id="${member.id}ActiveHp" class="hp-bar"></div>
                    </div>
                </div>
            </div>
          `;

          popup.appendChild(card);
      });
    }
      members.sort((a, b) => a.sortName.localeCompare(b.sortName));
      displayMembers(members);

      const hpPercentage = (members[0].currentHp / members[0].maxHp) * 100;
      const hpBar = card.querySelector(`#${members.id}ActiveHp`);

      console.log(members[0].currentHp, members[0].maxHp);

      function setHp(percentage, hp) {
        hp.style.width = (percentage) + "%";
        if (percentage <= 25) {
          hp.style.backgroundColor = hpBarRed;
        } else if (percentage <= 50) {
          hp.style.backgroundColor = hpBarYellow;
        } else {
          hp.style.backgroundColor = hpBarGreen;
        }
      };
      
      setHp(hpPercentage, hpBar);
    }
  }
);
@font-face {
  font-family: False-Positive;
  src: url(./fonts/falsepos.ttf);
}

body {
  margin: 0;
  padding: 0;
  border: 0;
  font-family: False-Positive;
  color: #333333;
}

.hide {
  display: none;
}

.popup {
  width: 500px;
  height: 800px;
  background-color: #e5e4c5ff;
  display: grid;
  grid-template-rows: repeat(6, 1fr);
  align-items: center;
  justify-content: center;
}

.pokemon-card {
  position: relative;
  width: 300px;
  height: 100px;
  background-color: #ffffcc;
  display: flex;
  overflow: hidden;
  align-items: center;
  border: #777777 solid 2px;
}
  
.sprite {
  min-width: 100px;
  width: 100px;
  height: 100px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  position: relative;
  align-items: center;
  justify-content: center;
}
  
.pokemon-sprite {
  grid-column: 1 / span 3;
  grid-row: 1 / span 3;
  max-width: 85%;
  margin-left: 5px;
}
  
#memberOneForm, #memberTwoForm, #memberThreeForm, #memberFourForm, #memberFiveForm, #memberSixForm {
  grid-column: 1;
  grid-row: 1;
  z-index: 1;
  width: 100%;
}
  
.stats {
  max-width: 100%;
  height: 100%;
  display: grid;
  grid-template-rows: 30px 16px 20px 15px;
  gap: 3px;
  padding-top: 5px;
}
  
.line-one, .line-two, .line-three, .line-four {
  display: flex;
  width: 200px;
}
  
.line-one {
  justify-content: space-between;
  align-items: center;
}
  
.line-three  {
  justify-content: end;
}
  
.name {
  min-width: 0;
  font-size: 30px;
}
  
p {
  margin: 0;
}

.level-container {
  display: flex;
  flex-direction: column;
  align-items: end;
  padding-right: 5px;
}

#level-text {
  font-size: 10px;
}

.level-number {
  font-size: 18px;
}

.types {
  width: 75%;
  height: 100%;
  display: flex;
  gap: 5px;
}

#type-one, #type-two {
  max-height: 100%;
  max-width: 100%;
}

.status {
  border-radius: 20px;
  margin-right: 3px;
  margin-top: 2px;
}

.hp-container {
  width: 100%;
  height: 100%;
  background-color: #555555;
  border-radius: 10px;
  border: 2px solid black;
  margin-right: 3px;
  overflow: hidden;
}

.hp-bar {
  width: 100%;
  height: 100%;
  transition: width 0.2s linear;
}
<div class='popup'></div>

2

Answers


  1. As @Bremer said, the problem is that you initialize the arrays inside the function and then every time you call the function – they return to their initial values.
    The solution is to take the arrays out of the function so that they are global variables

    const hpBarGreen = "#39ff7b";
    const hpBarYellow = "#f3b200";
    const hpBarRed = "#fb3041";
    const cardColor = "#ffffcc";
       const party = [{
        currentHP: 90,
        maxHP: 100
      }];
      const members = [{
        cardId: "cardOne",
        currentHp: party[0].currentHP,
        maxHp: party[0].maxHP
      }];
    function subtractHp() {
       
    
      const popup = document.querySelector(".popup");
      popup.innerHTML = "";
    
      members.forEach((member) => {
        const card = document.createElement("div");
        card.className = "pokemon-card";
        card.id = member.cardId;
    
        const stats = document.createElement("div");
        stats.className = "stats";
    
        const lineOne = document.createElement("div");
        lineOne.className = "line-one";
    
        const lineTwo = document.createElement("div");
        lineTwo.className = "line-two";
    
        const lineThree = document.createElement("div");
        lineThree.className = "line-three";
    
        const lineFour = document.createElement("div");
        lineFour.className = "line-four";
    
        const hpContainer = document.createElement("div");
        hpContainer.className = "hp-container";
    
        const hp = document.createElement("div");
        hp.className = "hp-bar";
        hp.id = member.cardId + "Hp";
    
        member.currentHp -= 40;
    
        const hpPercentage = (member.currentHp / member.maxHp) * 100;
    
        function setHp(percentage) {
          hp.style.width = percentage + "%";
          if (percentage <= 25) {
            hp.style.backgroundColor = hpBarRed;
          } else if (percentage <= 50) {
            hp.style.backgroundColor = hpBarYellow;
          } else {
            hp.style.backgroundColor = hpBarGreen;
          }
        }
    
        popup.appendChild(card);
        card.appendChild(stats);
        stats.appendChild(lineOne);
        stats.appendChild(lineTwo);
        stats.appendChild(lineThree);
        stats.appendChild(lineFour);
        lineFour.appendChild(hpContainer);
        hpContainer.appendChild(hp);
    
        setHp(hpPercentage);
      });
    }
    @font-face {
      font-family: False-Positive;
      src: url(./fonts/falsepos.ttf);
    }
    
    body {
      margin: 0;
      padding: 0;
      border: 0;
      font-family: False-Positive;
      color: #333333;
    }
    
    .hide {
      display: none;
    }
    
    .popup {
      width: 500px;
      height: 800px;
      background-color: #e5e4c5ff;
      display: grid;
      grid-template-rows: repeat(6, 1fr);
      align-items: center;
      justify-content: center;
    }
    
    .pokemon-card {
      position: relative;
      width: 300px;
      height: 100px;
      background-color: #777777;
      display: flex;
      overflow: hidden;
      align-items: center;
      border: #777777 solid 2px;
    }
    
    .sprite {
      min-width: 100px;
      width: 100px;
      height: 100px;
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-template-rows: repeat(3, 1fr);
      position: relative;
      align-items: center;
      justify-content: center;
    }
    
    .pokemon-sprite {
      grid-column: 1 / span 3;
      grid-row: 1 / span 3;
      max-width: 85%;
      margin-left: 5px;
    }
    
    #memberOneForm,
    #memberTwoForm,
    #memberThreeForm,
    #memberFourForm,
    #memberFiveForm,
    #memberSixForm {
      grid-column: 1;
      grid-row: 1;
      z-index: 1;
      width: 100%;
    }
    
    .stats {
      max-width: 100%;
      height: 100%;
      display: grid;
      grid-template-rows: 30px 16px 20px 15px;
      gap: 3px;
      padding-top: 5px;
    }
    
    .line-one,
    .line-two,
    .line-three,
    .line-four {
      display: flex;
      width: 200px;
    }
    
    .line-one {
      justify-content: space-between;
      align-items: center;
    }
    
    .line-three {
      justify-content: end;
    }
    
    .name {
      min-width: 0;
      font-size: 30px;
    }
    
    p {
      margin: 0;
    }
    
    .level-container {
      display: flex;
      flex-direction: column;
      align-items: end;
      padding-right: 5px;
    }
    
    #level-text {
      font-size: 10px;
    }
    
    .level-number {
      font-size: 18px;
    }
    
    .types {
      width: 75%;
      height: 100%;
      display: flex;
      gap: 5px;
    }
    
    #type-one,
    #type-two {
      max-height: 100%;
      max-width: 100%;
    }
    
    .status {
      border-radius: 20px;
      margin-right: 3px;
      margin-top: 2px;
    }
    
    .hp-container {
      width: 100%;
      height: 100%;
      background-color: #555555;
      border-radius: 10px;
      border: 2px solid black;
      margin-right: 3px;
      overflow: hidden;
    }
    
    .hp-bar {
      width: 100%;
      height: 100%;
    }
    <div class="popup"></div>
    <button onClick="subtractHp()">Update</button>
    Login or Signup to reply.
  2. your code was hard to dissect..
    The sequences of value assignments are strange,
    and you placed the function setHp() inside the loop members.forEach( member => !

    Here is what I understand about it, as well as a simpler(?) way to do this….

    const 
      hpBarGreen  = '#39ff7b'
    , hpBarYellow = '#f3b200'
    , hpBarRed    = '#fb3041'
    , cardColor   = '#ffffcc'  // ?
    , party       = [{ currentHP: 90,  maxHP: 100 }]
    , members     = [{ cardId: 'cardOne', currentHp: party[0].currentHP, maxHp: party[0].maxHP }]
    , popup       = document.querySelector('.popup');
      ;
    
    function subtractHp()
      {
      popup.innerHTML = '';
      members.forEach( member =>
        {
        let 
          hpPercentage = (member.currentHp / member.maxHp) * 100
        , fragHTML     = new Range().createContextualFragment(`
            <div class="pokemon-card" id="${member.cardId}">
              <div class="stats">
                <div class="line-one"></div>
                <div class="line-two"></div>
                <div class="line-three"></div>
                <div class="line-four">
                  <div class="hp-container">
                    <div class="hp-bar" id="${member.cardId}Hp" style=""></div>
                  </div>
                </div>
              </div>
            </div>`);
    
        setHp( fragHTML.querySelector(`#${member.cardId}Hp`), hpPercentage );
        
        popup.append(fragHTML);
        member.currentHp -= 40;
        });
    
      function setHp( hp_x, percentage )    // OUTSIDE THE LOOP members.forEach( member => 
        {
        hp_x.style.width           = percentage + '%';
        hp_x.style.backgroundColor = (percentage <= 25) ? hpBarRed
                                   : (percentage <= 50) ? hpBarYellow
                                   : hpBarGreen;
        }
      }
    @font-face {
      font-family : False-Positive;
      src         : url(./fonts/falsepos.ttf);
      }
    body {
      margin      : 0;
      padding     : 0;
      border      : 0;
      font-family : False-Positive;
      color       : #333333;
      }
    .hide {
      display : none;
      }
    .popup {
      width              : 500px;
      height             : 800px;
      background-color   : #e5e4c5ff;
      display            : grid;
      grid-template-rows : repeat(6, 1fr);
      align-items        : center;
      justify-content    : center;
      }
    .pokemon-card {
      position         : relative;
      width            : 300px;
      height           : 100px;
      background-color : #777777;
      display          : flex;
      overflow         : hidden;
      align-items      : center;
      border           : #777777 solid 2px;
      }
    .sprite {
      min-width             : 100px;
      width                 : 100px;
      height                : 100px;
      display               : grid;
      grid-template-columns : repeat(3, 1fr);
      grid-template-rows    : repeat(3, 1fr);
      position              : relative;
      align-items           : center;
      justify-content       : center;
      }
    .pokemon-sprite {
      grid-column : 1 / span 3;
      grid-row    : 1 / span 3;
      max-width   : 85%;
      margin-left : 5px;
      }
    #memberOneForm,
    #memberTwoForm,
    #memberThreeForm,
    #memberFourForm,
    #memberFiveForm,
    #memberSixForm {
      grid-column : 1;
      grid-row    : 1;
      z-index     : 1;
      width       : 100%;
      }
    .stats {
      max-width          : 100%;
      height             : 100%;
      display            : grid;
      grid-template-rows : 30px 16px 20px 15px;
      gap                : 3px;
      padding-top        : 5px;
      }
    .line-one,
    .line-two,
    .line-three,
    .line-four {
      display : flex;
      width   : 200px;
      }
    .line-one {
      justify-content : space-between;
      align-items     : center;
      }
    .line-three {
      justify-content : end;
      }
    .name {
      min-width : 0;
      font-size : 30px;
      }
    p {
      margin : 0;
      }
    .level-container {
      display        : flex;
      flex-direction : column;
      align-items    : end;
      padding-right  : 5px;
      }
    #level-text {
      font-size : 10px;
      }
    .level-number {
      font-size : 18px;
      }
    .types {
      width   : 75%;
      height  : 100%;
      display : flex;
      gap     : 5px;
      }
    #type-one,
    #type-two {
      max-height : 100%;
      max-width  : 100%;
      }
    .status {
      border-radius : 20px;
      margin-right  : 3px;
      margin-top    : 2px;
      }
    .hp-container {
      width            : 100%;
      height           : 100%;
      background-color : #555555;
      border-radius    : 10px;
      border           : 2px solid black;
      margin-right     : 3px;
      overflow         : hidden;
      }
    .hp-bar {
      width  : 100%;
      height : 100%;
      }
    <div class="popup"></div>
    <button onClick="subtractHp()">Update</button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search