How to make :before & :after pseudo-elements fill remaining width?

Viewed 858

So I'm trying to edit this existing widget on a page. I don't have access to edit the source code, so want to be able to do it entirely in CSS, ideally. Rather than trying to explain, I think it's easier just to paste an image of the desired behaviour - i.e. there is a left section (:before) and right section (:after) - as the splitter moves, then the widths of each pseudo element should change accordingly.

desired result

Here is a JSFiddle of a bare-bones version of the widget's code/structure

https://jsfiddle.net/o0zgyut3/

HTML

<div id='wrapper'>
  <div id='splitter' style='left: 50%'></div>
</div>
<input id='slider' type='range' min='0' max='100' value='50'/>

CSS

body, html{
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  background: #222;
}

input{
  margin-top: 10px;
}

#wrapper{
  width: 500px;
  height: 50px;
  background: #444;
  position: relative;
  display: flex;
}

/*left*/
#wrapper::before{
  content:'';
  width: 50px;
  height: 50px;
  background: linear-gradient(0deg,#00be1abf,#5fed00b3);
  position: absolute;
  left: 0px;
}

/*right*/
#wrapper::after{
  content:'';
  width: 50px;
  height: 50px;
  background: linear-gradient(0deg,#9f0000ff,#f10000ff);
  position: absolute;
  right: 0px;
}

#splitter{
  position: relative;
  transform: translateX(-50%);
  background: #ddd;
  height: 50px;
  width: 10px;
  z-index: 99;
}

JS (note that I wouldn't have access to the JS in the real site - the slider in this example below is just to allow changing the splitter position easily while debugging (without having to add the drag functionality)

document.getElementById("slider").addEventListener("change", function(){
  document.getElementById("splitter").style.left = this.value + "%";
})

I have tried various approaches (with flex, inline, grid, floats, margins etc) and could get behaviour that was half-way to what I wanted, but never exactly. Also note, that, if it's easier, the real widths don't have to be accurate - it's a purely visual widget, so it's fine to e.g. make the right section 100% width, then only the left section is dynamic and position it above the right by giving it a higher z-index, if that's easier - i.e. if the right section was hard-coded at 100% width, and the left derived an 80% width from the css, it would give the illusion of an 80:20 split (even though it'd technically be 80:100), which is fine

Anyway, I suspect I'm missing something fairly obvious, so if anyone is able to get this working, that'd be awesome. Thanks

1 Answers

It's not possible that you directly access pseudo-elements with JS as when the page loads they're not part of the DOM.

However, you can do something like this create a new style element which will have new CSS for your wrapper

Run snippet below to see it working.

document.getElementById("slider").addEventListener("change", function() {
  document.getElementById("splitter").style.left = this.value + "%";
  var sytleElement = document.head.appendChild(document.createElement("style"));
  sytleElement.innerHTML = "#wrapper:before {width: " + this.value + '%' + "  }";
})
body,
html {
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  background: #222;
}

input {
  margin-top: 10px;
}

#wrapper {
  width: 500px;
  height: 50px;
  background: #444;
  position: relative;
  display: flex;
}


/*left*/

#wrapper::before {
  content: '';
  width: 10%;
  height: 50px;
  background: linear-gradient(0deg, #00be1abf, #5fed00b3);
  position: absolute;
  left: 0px;
}


/*right*/

#wrapper::after {
  content: '';
  width: 50px;
  height: 50px;
  background: linear-gradient(0deg, #9f0000ff, #f10000ff);
  position: absolute;
  right: 0px;
}

#splitter {
  position: relative;
  transform: translateX(-50%);
  background: #ddd;
  height: 50px;
  width: 10px;
  z-index: 99;
}
<div id='wrapper'>
  <div id='splitter' style='left: 10%'></div>
</div>
<input id='slider' type='range' min='0' max='100' value='10' />

Related