1

I have an input field that accepts a percent, but I'd like to indicate to the user that the value they are entering is a percent.

The problem I'm having is that I'd like the percent to lead the text, so in the input it looks like this: 100% or 10000%, but I don't want the % to be part of the actual input.

Is there a way to achieve? I've tried a couple of options to do this with CSS but fall short when the % value doesn't actually adjust the placement in the input. Instead it's always appended to the very end of the input field instead of catching up with the input.

The component looks like this:

class Input extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isActive: false,
    }
  }

  render() {
    return (
    <div>
      <input 
        value={this.props.value} >
      </input>
    </div>
    )
  }
}

CSS:

div {
  input:after {
    content: '%';
    position: absolute;
    top: 3.4rem;
    z-index: 9999;
    color: blue;
    font-size: 2rem;

  }
}

Example of what I'm trying to achieve, the highlighted text in the example below is what I want the user to be able to edit, and the % to dynamically move depending on the length of the input.

enter image description here

enter image description here

James Ives
  • 3,177
  • 3
  • 30
  • 63
  • 2
    imho, i would just do it with css and fix it to the right most part of the input, the only other option is to make it a controlled input and have it start as '%' and then just parse it for the value – Eric Hasselbring Mar 05 '19 at 19:50
  • This is not the exact same question, but `:before and :after render inside a container and can not contain other elements.` Source: https://stackoverflow.com/questions/2587669/can-i-use-a-before-or-after-pseudo-element-on-an-input-field – Arthur Mar 05 '19 at 19:50
  • Biggest problem with that @EricHasselbring is that I can then no longer restrict the type of input into the field, ie `type="number"`. I'm guessing what I'm trying to do is not possible which is a bit of a bummer. – James Ives Mar 05 '19 at 20:03
  • yeah, that is a bummer.. i guess the only other thing, is to use a monospaced font and then you could use js to move the % sign over after an input, since its a monospaced font it would move the same exact amount of pixels each additional char – Eric Hasselbring Mar 05 '19 at 20:10
  • My comment tells you how to do it... You just have to convert it to regular JS. You just use regex to replace all but numbers & just add a % to the end, and it formats it properly every time they type to make sure :) – Nerdi.org Mar 05 '19 at 20:13

2 Answers2

4

It's easy by using data-attribute on a container (because input cannot handle :before and :after, [More here]) and CSS content:attr()

update = function(el) {
  el.parentElement.setAttribute('data-value', el.value)
}
div[data-value] {
  position: relative;
}
div[data-value]:after {
  content: attr(data-value) "%";
  position: absolute;
  top: 2px; /* Borders */
  left: 2px; /* Borders */
}
div[data-value] input {
  color: #FFF; /* Optional bug avoid visual bugs */
}
<div data-value="0.01">
  <input value="0.01" oninput="update(this)"/>
</div>

PS: I did it on vanillia but it's easy to update in React

Arthur
  • 4,870
  • 3
  • 32
  • 57
  • 2
    great answer, this should be used much more often than other common workarounds – Symphony0084 Dec 11 '19 at 17:17
  • Run code snippet, type anything until you run off the right side of the input. This doesn't lock to the edge and scroll like you'd expect because the container div doesn't know anything about being a proper text input. – Ionoclast Brigham Oct 26 '22 at 20:05
-2

Using jQuery:

$("#input").on("keyup", function(){
  $(this).val($(this).val().replace(/[^0-9]/gi, '') + '%')
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="input"/>

This would make it so there's always a % sign at the end and they cannot remove it or add anything except for numbers!

You can also convert this to regular Javascript if you wanted to

Arthur
  • 4,870
  • 3
  • 32
  • 57
Nerdi.org
  • 895
  • 6
  • 13