skip to Main Content

I have a condition that when the input value is greater than 100, another bottle is added, but I don’t know where to insert another function so that the second bottle displays the water level I need.
I was trying to add a new function to a function addBottle2, but only got errors

let inputWaterLevel = document.querySelector('[name=water-level]');
let heightLiquid = +tubeLiquid.getAttribute('height');
let yLiquid = +tubeLiquid.getAttribute('y');
let liquidPercent = heightLiquid/100;

inputWaterLevel.addEventListener('input', updateWaterLevel);

function updateWaterLevel() {
  let value = +this.value;
  
  let height = liquidPercent * value;
  let y = yLiquid + (heightLiquid-height);
  
  tubeLiquid.setAttribute('height', liquidPercent * value )
  tubeLiquid.setAttribute('y', y )

}

let addBottle2 = () => {
    inputWaterLevel.id = 'inpId';
    let inpId = document.getElementById('inpId')
    inpId.addEventListener("input", () => {
        if (inpId.value > 100) {
            document.getElementById("bot2").style.display = 'block';
        }
        else if (inpId.value <= 100){
            document.getElementById("bot2").style.display = 'none';
        }
    });
}
addBottle2()
 
 .cont{
     display: flex;
 }

 #bot2{
     display: none;
 }
<p><input type="number" min="0" max="200" value="100" name="water-level" /></p>

<div class="cont">
    <div class="bot1">
        <svg viewBox="0 0 300 300" width="150">
            <clipPath id="clip">
                <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
            </clipPath>
            <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
            <rect clip-path="url(#clip)" id="tubeLiquid" x="115" y="58" width="70" height="233" fill="#74ccf4" />
        </svg>
    </div>

    <div id="bot2">
        <svg viewBox="0 0 300 300" width="150">
            <clipPath id="clip2">
                <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
            </clipPath>
            <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
            <rect clip-path="url(#clip2)" id="tubeLiquid2" x="115" y="58" width="70" height="233" fill="#74ccf4" />
        </svg>
    </div>

</div>

4

Answers


  1. What you can do is re-use the code to fill the first bottle, but with parameters telling which bottle to update, and which value to subtract from the total percentage, something like this:

    let inputWaterLevel = document.querySelector('[name=water-level]');
    let heightLiquid = +tubeLiquid.getAttribute('height');
    let yLiquid = +tubeLiquid.getAttribute('y');
    let liquidPercent = heightLiquid/100;
    
    //tell this code to update bottle 1 without offset
    inputWaterLevel.addEventListener('input', function(){
        updateWaterLevel('tubeLiquid', 0);
    });
    
    function updateWaterLevel(tubeLiquidId, offset) {
      let value = +inputWaterLevel.value - offset; //using input variable because "this" will be not be the input anymore now the call has changed / sutract offset
      
      let height = liquidPercent * value;
      let y = yLiquid + (heightLiquid-height);
      
      let tubeLiquid = document.getElementById(tubeLiquidId); //get dynamically liquid to update
      tubeLiquid.setAttribute('height', liquidPercent * value )
      tubeLiquid.setAttribute('y', y )
    
    }
    
    let addBottle2 = () => {
        //those 2 lines are useless, you already have access to "inputWaterLevel" from outer scope, just use it
        //inputWaterLevel.id = 'inpId';
        //let inpId = document.getElementById('inpId')
        inputWaterLevel.addEventListener("input", () => {
            if (inputWaterLevel.value > 100) {
                document.getElementById("bot2").style.display = 'block';
                updateWaterLevel('tubeLiquid2', 100); //the call to update bottle 2 telling the function to remove 100
            }
            else if (inputWaterLevel.value <= 100){
                document.getElementById("bot2").style.display = 'none';
                //no need to update bottle 2 if we're going to hide it
            }
        });
    }
    addBottle2()
     
     .cont{
         display: flex;
     }
    
     #bot2{
         display: none;
     }
    <p><input type="number" min="0" max="200" value="100" name="water-level" /></p>
    
    <div class="cont">
        <div class="bot1">
            <svg viewBox="0 0 300 300" width="150">
                <clipPath id="clip">
                    <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
                </clipPath>
                <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
                <rect clip-path="url(#clip)" id="tubeLiquid" x="115" y="58" width="70" height="233" fill="#74ccf4" />
            </svg>
        </div>
    
        <div id="bot2">
            <svg viewBox="0 0 300 300" width="150">
                <clipPath id="clip2">
                    <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
                </clipPath>
                <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
                <rect clip-path="url(#clip2)" id="tubeLiquid2" x="115" y="58" width="70" height="233" fill="#74ccf4" />
            </svg>
        </div>
    
    </div>

    In theory you could update the second bottle using only one event listener, but I didn’t want to modifiy your code more than necessary. When you can, don’t add 2 listeners to the same input when you can put your code in a single one, it makes the code easier to read

    Also your function addBottle2 is not properly named, as it doesn’t add a second bottle, it just sets the listener to it, and then shouldn’t be called twice.

    You have also minor optimisations that can be done. Note also that as it is, the code won’t support different size bottles, as the function uses the values calculated for the first bottle’s height

    Login or Signup to reply.
  2. First, don’t refer to the element using its id directly as an variable. See this post. Always use proper method to get the element from the DOM.

    Second, the addBottle2 function is redundant.

    inputWaterLevel.id = 'inpId';
    let inpId = document.getElementById('inpId')
    

    These two lines are redundant because you have already get the input element in inputWaterLevel. Changing its id and get it again by id do nothing useful. Just use it as is.

    inpId.addEventListener("input", () => {...
    

    This is also redundant because you have already added the event listener updateWaterLevel. You can simply add code in updateWaterLevel

    let inputWaterLevel = document.querySelector('[name=water-level]');
    //get tubLiquid properly
    let tubeLiquid = document.getElementById("tubeLiquid");
    let heightLiquid = +tubeLiquid.getAttribute('height');
    let yLiquid = +tubeLiquid.getAttribute('y');
    let liquidPercent = heightLiquid/100;
    
    //duplicate for second bottle, unless assuming two bottle always have the same size hardcoded.
    let tubeLiquid2 = document.getElementById("tubeLiquid2");
    let heightLiquid2 = +tubeLiquid2.getAttribute('height');
    let yLiquid2 = +tubeLiquid2.getAttribute('y');
    let liquidPercent2 = heightLiquid2/100;
    let bot2 = document.getElementById("bot2");
    
    inputWaterLevel.addEventListener('input', updateWaterLevel);
    
    function updateWaterLevel() {
      let value = +this.value;
      
      let height = liquidPercent * value;
      let y = yLiquid + (heightLiquid-height);
      
      tubeLiquid.setAttribute('height', liquidPercent * value );
      tubeLiquid.setAttribute('y', y );
    
      //bottle2
      if(value > 100)
      {
        let value2 = value - 100;
        let height2 = liquidPercent2 * value2;
        let y2 = yLiquid2 + (heightLiquid2-height2);
    
        tubeLiquid2.setAttribute('height', liquidPercent2 * value2 );
        tubeLiquid2.setAttribute('y', y2 );
        
        bot2.style.display = "block";
      }  
      else
      {
        bot2.style.display = "none";
      }
    }
    .cont{
         display: flex;
     }
    
     #bot2{
         display: none;
     }
    <p><input type="number" min="0" max="200" value="100" name="water-level" /></p>
    
    <div class="cont">
        <div class="bot1">
            <svg viewBox="0 0 300 300" width="150">
                <clipPath id="clip">
                    <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
                </clipPath>
                <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
                <rect clip-path="url(#clip)" id="tubeLiquid" x="115" y="58" width="70" height="233" fill="#74ccf4" />
            </svg>
        </div>
    
        <div id="bot2">
            <svg viewBox="0 0 300 300" width="150">
                <clipPath id="clip2">
                    <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
                </clipPath>
                <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
                <rect clip-path="url(#clip2)" id="tubeLiquid2" x="115" y="58" width="70" height="233" fill="#74ccf4" />
            </svg>
        </div>
    
    </div>
    Login or Signup to reply.
  3. You don’t need a second function to achieve that.

    You can check: when the input value is greater than 100, the height and position of tubeLiquid2 are calculated based on the remaining value (value – 100), and the bot2 div is displayed.

    let inputWaterLevel = document.querySelector('[name=water-level]');
    let tubeLiquid = document.getElementById('tubeLiquid');
    let tubeLiquid2 = document.getElementById('tubeLiquid2');
    let bottle2 = document.getElementById('bot2');
    let liquidPercent = tubeLiquid.getAttribute('height') / 100;
    let yLiquid = +tubeLiquid.getAttribute('y')
    let heightLiquid = +tubeLiquid.getAttribute('height');
    
    inputWaterLevel.addEventListener('input', updateWaterLevel);
    
    function updateWaterLevel() {
      let value = +this.value;
    
      if (value <= 100) {
        let height = liquidPercent * value;
        let y = yLiquid + (heightLiquid - height);
    
        tubeLiquid.setAttribute('height', height);
        tubeLiquid.setAttribute('y', y);
        tubeLiquid2.setAttribute('height', 0);
        bottle2.style.display = 'none';
      } else {
        let height1 = liquidPercent * 100;
        let y1 = yLiquid + (heightLiquid - height1);
    
        let height2 = liquidPercent * (value - 100);
        let y2 = yLiquid + (heightLiquid - height2);
    
        tubeLiquid.setAttribute('height', height1);
        tubeLiquid.setAttribute('y', y1);
        tubeLiquid2.setAttribute('height', height2);
        tubeLiquid2.setAttribute('y', y2);
        bottle2.style.display = 'block';
      }
    }
    .cont{
         display: flex;
     }
    
     #bot2{
         display: none;
     }
    <p><input type="number" min="0" max="200" value="100" name="water-level" /></p>
    
    <div class="cont">
      <div id="bot1">
        <svg viewBox="0 0 300 300" width="150">
          <clipPath id="clip">
            <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
          </clipPath>
          <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
          <rect clip-path="url(#clip)" id="tubeLiquid" x="115" y="58" width="70" height="233" fill="#74ccf4" />
        </svg>
      </div>
    
      <div id="bot2">
        <svg viewBox="0 0 300 300" width="150">
          <clipPath id="clip2">
            <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
          </clipPath>
          <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
          <rect clip-path="url(#clip2)" id="tubeLiquid2" x="115" y="58" width="70" height="233" fill="#74ccf4" />
        </svg>
      </div>
    </div>
    Login or Signup to reply.
  4. Water Level Task

    Create a second function to manage the water level of the second bottle. Lets call the new function updateWaterLevel2(). We will use a very similar logic to updateWaterLevel(), however it will operate on tubeLiquid2 element instead.

    In updateWaterLevel I will add a conditional check when the value goes over 100 and call updateWaterLevel2() with the excess amount. Inside the updateWaterLevel2() function, we will adjust the height and y attributes of tubeLiquid2 just like in tubeLiquid.

    Also you need to select the tubeLiquid2 element from the DOM and get the initial height and y variable values. Those values are needed to calculate the new height and y positions of the liquid inside the second bottle.

    // Select elements from the DOM
    let inputWaterLevel = document.querySelector('[name=water-level]');
    let tubeLiquid = document.querySelector('#tubeLiquid');
    let tubeLiquid2 = document.querySelector('#tubeLiquid2');
    
    // Get the initial attributes from the first SVG element
    let heightLiquid = +tubeLiquid.getAttribute('height');
    let yLiquid = +tubeLiquid.getAttribute('y');
    let liquidPercent = heightLiquid / 100;
    
    // Get the initial attributes from the second SVG element
    let heightLiquid2 = +tubeLiquid2.getAttribute('height');
    let yLiquid2 = +tubeLiquid2.getAttribute('y');
    let liquidPercent2 = heightLiquid2 / 100;
    
    // Add an event listener to your input field
    inputWaterLevel.addEventListener('input', updateWaterLevel);
    
    // This is the function to update the water level inside the first bottle
    function updateWaterLevel() {
      let value = +this.value;
    
      let height = liquidPercent * value;
      let y = yLiquid + (heightLiquid - height);
    
      // Set new attributes to first SVG element
      tubeLiquid.setAttribute('height', liquidPercent * value)
      tubeLiquid.setAttribute('y', y)
    
      // Check if value is greater than 100 and update second bottle value
      if (value > 100) {
        updateWaterLevel2(value - 100);
      } else {
        tubeLiquid2.setAttribute('height', 0);
      }
    }
    
    // The function to update water level inside the second bottle
    function updateWaterLevel2(value) {
      let height = liquidPercent2 * value;
      let y = yLiquid2 + (heightLiquid2 - height);
    
      // Set the new attributes to the second SVG element
      tubeLiquid2.setAttribute('height', liquidPercent2 * value)
      tubeLiquid2.setAttribute('y', y)
    }
    
    // Function to show or hide the second bottle depending on the input
    let addBottle2 = () => {
      inputWaterLevel.id = 'inpId';
      let inpId = document.getElementById('inpId')
      inpId.addEventListener("input", () => {
        if (inpId.value > 100) {
          document.getElementById("bot2").style.display = 'block';
        } else {
          document.getElementById("bot2").style.display = 'none';
        }
      });
    }
    
    // Call function to initialize bottle display
    addBottle2();
    .cont {
      display: flex;
    }
    
    #bot2 {
      display: none;
    }
    <p><input type="number" min="0" max="200" value="100" name="water-level" /></p>
    
    <div class="cont">
      <div class="bot1">
        <svg viewBox="0 0 300 300" width="150">
                <clipPath id="clip">
                    <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
                </clipPath>
                <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
                <rect clip-path="url(#clip)" id="tubeLiquid" x="115" y="58" width="70" height="233" fill="#74ccf4" />
            </svg>
      </div>
    
      <div id="bot2">
        <svg viewBox="0 0 300 300" width="150">
                <clipPath id="clip2">
                    <rect x="118" y="68" width="64" height="228" rx="20" ry="20" />
                </clipPath>
                <image width="300" xlink:href="https://i.ibb.co/HGFQYTj/bottle.png" />
                <rect clip-path="url(#clip2)" id="tubeLiquid2" x="115" y="58" width="70" height="233" fill="#74ccf4" />
            </svg>
      </div>
    
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search