1

Below is the demo where I am able to move the middle pointer using javascript. But I am unable to move the line which connects it from the start. Seems like I cannot give width property to :after element of progress. What can I do here to achieve my goal. Please help

function myFunction(){
   var middleDot = document.querySelector('.is-active');
  var textinput = document.querySelector('#textinput');
middleDot.style.left = textinput.value+'%'
}
.progress-wrapper {
  margin: 0 32px;
}
.progress {
        padding: 40px 0;
        position: relative;
  width:100%
      }
      .progress::before,
      .progress::after {
        content: "";
        background-color: #dfe3e4;
        height: 5px;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        left: 0;
      }
      .progress::before {
        width: 100%;
      }
      .progress::after {
        width: 50%;
        background: #5D50DB;
      }
      .progress li {
        position: relative;
        list-style: none;
        z-index: 1;
        display: inline-block;
        text-align:center;
      }
      .progress li:first-child {
        left: 0;
      }
      .progress li.is-active {
        left: 50%;
        position: absolute;
      }
      .progress li:last-child {
        float: right;
      }
      .progress > li:before {
        content: "";
        display: block;
        margin: 0 auto;
        background: #dfe3e4;
        width: 20px;
        height: 20px;
        text-align: center;
        line-height: 20px;
        border-radius: 100%;
        position: relative;
        z-index: 1000;
      }
      .progress li:first-child:before {
        background: #5D50DB;
        
      }
      .progress li.is-active:before {
        background: #5D50DB;
      }
      .progress li span {
        position: absolute;
        bottom: -20px;
        width: max-content;
        left: 50%;
    transform: translateX(-50%);
        
      }
.is-active span{
  top: -20px;;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <input type="text" id="textinput" value=50 onkeyup="myFunction()">
<div class="progress-wrapper">
      <ol class="progress">
      <li class="is-complete" data-step="1"><span>Place A</span></li>
      <li class="is-active" data-step="2"><span>Place B</span></li>
      <li data-step="3" class="progress__last"><span>Place C</span></li>
    </ol>
  </div>
</body>
</html>
Ben
  • 25
  • 5

2 Answers2

2

You can implement this by adding new style tag into head tag dynamically.

The new style tag will contain the css info regarding width changes of progress.

function myFunction(){
  var middleDot = document.querySelector('.is-active');
  var textinput = document.querySelector('#textinput');
  middleDot.style.left = textinput.value+'%';
  
  var progressStyle = document.createElement('style');
  progressStyle.innerHTML = `.progress:after {
    width: ${textinput.value}%
  }`;
  document.head.appendChild(progressStyle);
}
.progress-wrapper {
  margin: 0 32px;
}
.progress {
        padding: 40px 0;
        position: relative;
  width:100%
      }
      .progress::before,
      .progress::after {
        content: "";
        background-color: #dfe3e4;
        height: 5px;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        left: 0;
      }
      .progress::before {
        width: 100%;
      }
      .progress::after {
        width: 50%;
        background: #5D50DB;
      }
      .progress li {
        position: relative;
        list-style: none;
        z-index: 1;
        display: inline-block;
        text-align:center;
      }
      .progress li:first-child {
        left: 0;
      }
      .progress li.is-active {
        left: 50%;
        position: absolute;
      }
      .progress li:last-child {
        float: right;
      }
      .progress > li:before {
        content: "";
        display: block;
        margin: 0 auto;
        background: #dfe3e4;
        width: 20px;
        height: 20px;
        text-align: center;
        line-height: 20px;
        border-radius: 100%;
        position: relative;
        z-index: 1000;
      }
      .progress li:first-child:before {
        background: #5D50DB;
        
      }
      .progress li.is-active:before {
        background: #5D50DB;
      }
      .progress li span {
        position: absolute;
        bottom: -20px;
        width: max-content;
        left: 50%;
    transform: translateX(-50%);
        
      }
.is-active span{
  top: -20px;;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <input type="text" id="textinput" value=50 onkeyup="myFunction()">
<div class="progress-wrapper">
      <ol class="progress">
      <li class="is-complete" data-step="1"><span>Place A</span></li>
      <li class="is-active" data-step="2"><span>Place B</span></li>
      <li data-step="3" class="progress__last"><span>Place C</span></li>
    </ol>
  </div>
</body>
</html>
Derek Wang
  • 10,098
  • 4
  • 18
  • 39
  • that working Derek, could you also tell why the middle pointer crossing the last pointer when I put 100 in the input text – Ben Sep 30 '20 at 04:27
  • Checking the style, `.progress_last` point is inside the `.progress` ol tag. And `.is-active` point has `absolute` position so when setting 100%, it will be outside of ol. (left: 100% means start of right progress position.). – Derek Wang Sep 30 '20 at 04:45
  • Yes you are right. , Thanks Derek – Ben Sep 30 '20 at 07:02
2

I think it's hard to directly set the style of :after element, but you can add a new style rule to overwrite it's width property:

update: fix the bug when input value is 100.

function myFunction(){
   var middleDot = document.querySelector('.is-active');
   var textinput = document.querySelector('#textinput');
   middleDot.style.left = textinput.value+'%';
   document.styleSheets[0].addRule('.progress::after','width:' +  textinput.value+'% !important');
}
.progress-wrapper {
  margin: 0 32px;
}
.progress {
        padding: 40px 0;
        position: relative;
        width:100%;
        height: 20px;/*set this height to the value as the pointers' height*/
      }
      .progress::before,
      .progress::after {
        content: "";
        background-color: #dfe3e4;
        height: 5px;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        left: 0;
      }
      .progress::before {
        width: 100%;
      }
      .progress::after {
        width: 50%;
        background: #ff0000;
      }
      .progress li {
        position: relative;
        list-style: none;
        z-index: 1;
        display: inline-block;
        text-align:center;
      }
      .progress li:first-child {
        left: 0;
      }
      .progress li.is-active {
        left: 50%;
        z-index: 1001; /*need to be larger to overlap last one*/
        position: absolute;
      }
      .progress li:last-child {
        float: right;
        postion: relative;
        margin-right: -20px;/*give it a margin-right to let it overflow ol element too*/
      }
      .progress > li:before {
        content: "";
        display: block;
        margin: 0 auto;
        background: #dfe3e4;
        width: 20px;
        height: 20px;
        text-align: center;
        line-height: 20px;
        border-radius: 100%;
        position: relative;
        z-index: 1000;
      }
      .progress li:first-child:before {
        background: #5D50DB;
        
      }
      .progress li.is-active:before {
        background: #5D50DB;
      }
      .progress li span {
        position: absolute;
        bottom: -20px;
        width: max-content;
        left: 50%;
    transform: translateX(-50%);
        
      }
.is-active span{
  top: -20px;;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <input type="text" id="textinput" value=50 onkeyup="myFunction()">
<div class="progress-wrapper">
      <ol class="progress">
      <li class="is-complete" data-step="1"><span>Place A</span></li>
      <li class="is-active" data-step="2"><span>Place B</span></li>
      <li data-step="3" class="progress__last"><span>Place C</span></li>
    </ol>
  </div>
</body>
</html>
Qiu Zhou
  • 1,235
  • 8
  • 12
  • that working, could you also tell why the middle pointer crossing the last pointer when I put 100 in the input text – Ben Sep 30 '20 at 04:26
  • Because when you set the middle point's `left` to 100% of the width of `ol` process element, it will right overflow `ol` process element, but the last pointer doesn't and is still inside `ol` process element. – Qiu Zhou Sep 30 '20 at 04:59
  • ok I did transform: translateX(-100%); on is-active element. It works fine now. Is this solution good ? or is there a catch? – Ben Sep 30 '20 at 05:40
  • So will it work when you input 0 ? I think it will left overflow `ol` process element then. My suggestion is that you give `the last pointer` a `margin-right: -20px` to let it right overflow too, then it'll do the trick, see my updated answer above. – Qiu Zhou Sep 30 '20 at 05:49
  • hey your trick worked, I just had to give the margin right -18px and the pointer was showing little away from the middle line in my page. Is that fine? – Ben Sep 30 '20 at 07:01
  • the margin-right value should be the negative the pointer's width, a slight difference is fine, I can't see what it is like in your page, but in this question, it's because the pointers are not vertically centered, setting the `ol` process element's height to the same value as the pointers' height would solve the problem. see my updated answer above. – Qiu Zhou Sep 30 '20 at 07:30