0

Goal:

I have an input element in which the user should enter a number between 1 and 999, optionally may use the (input type="number") up/down spinner, but when the mouse isn't over this element or the element doesn't have the focus, I want the input element to be of 'text' type to ensure that the input is sized to only show the element's value without the spinner showing or the blank area of the hidden spinner. This way the value in the input element and the text to the right of the element doesn't move whether the element is of type 'text' or 'number', the element has the focus or not, or the mouse is over the element or not.

Background:

Currently, I'm initially setting the input element's type to 'text', margin-left to '2ch', and width to '4ch', then when the mouse is over the element, I set the element's type to 'number', margin-left to '0', and width to '6ch'. When the mouse moves off of the element or the element looses focus, I set these properties back to their initial values.

Code:

<style>
  /* Number text (1/3 etc) */
  .numbertext { color: #f2f2f2; font-size: 12px; padding: 8px 12px; position: absolute; top: -90px; }
</style>
<div class="numbertext">
  <input type="text" style="text-align: right; margin-left: 2ch; width: 4ch;" min="1" max="999" value="1"
         onmouseover="setInputType( 'number' );"
         mouseout="setInputType( 'text' );"
         onblur="setInputType( 'text' );" />/
  <span>999</span>
</div>
<script>
  function setInputType( type ) {
    var input = window.event.currentTarget;

    if( type === 'text' ) {

      input.style.marginLeft = '2ch'; 
      input.style.width      = '4ch';

    }
    else {

      input.style.marginLeft = '0'; 
      input.style.width      = '6ch';

    }
    input.type = type;
  }
</script>

Problem:

When the page initially shows, the code displays the way that I want, hovering the cursor over the field or focusing on it works, too. However, moving the mouse away from the field after the mouse hovered over the it, or the field looses the focus, inconsistently restores the initial setting of the input element because the mouseout and blur event handlers aren't always triggering. I know this because putting a breakpoint on the setInputType function's if( type === 'text' ) statement branch and running the code in the Chrome inspect > source panel doesn't stop the code execution when the mouse moves away from the element after it mouse was moved over it.

Any ideas about why the mouseout and blur event handlers aren't properly working consistently?

Solution:

This CodePage shows a fully working solution that includes Bryan Elliott's correction and Jon P's suggestion.

Thanks

  • On your `` you have `mouseout="..."`, It needs to be: `onmouseout="..."` – Bryan Elliott Feb 14 '20 at 01:54
  • Make sure to use `focus` as well – Jon P Feb 14 '20 at 05:38
  • Yup on the focus event, too, but for something different. I set a hasFocused custom property to true in the onfocus assignment and set this property to false in the onblur assignment. In the setInputType function I added an if( ( typeof( input.hasFocus ) === 'undefined' ) || ( !input.hasFocus ) ) test before the if( type === 'text' ) check to prevent the input's properties being changed when the input has the focus but the user moves the mouse away from the element. To the onblur assignment I added a call to a function that performs the button's work based on the value of the input element. –  Feb 14 '20 at 08:30

2 Answers2

1

On your element events, you have mouseout="...", It needs to be: onmouseout="..."

Like this:

<input
    type="text"
    style="text-align: right; margin-left: 2ch; width: 4ch;"
    min="1" max="999"
    value="1"
    onmouseover="setInputType( 'number' );"
    onmouseout="setInputType( 'text' );"   //notice the onmouseout=""  instead of mouseout=""
    onblur="setInputType( 'text' );"
/>
Bryan Elliott
  • 4,055
  • 2
  • 21
  • 22
  • @Howard Brown, did this answer fix your problem? – Bryan Elliott Feb 14 '20 at 02:16
  • @HowardBrown, ok awesome. Yeah, I totally understand, sometimes the smallest mistakes are the hardest to spot, and it just takes another set of eyes on the problem. Cool, well if my answer was helpful and answered your question, would you mind marking it as the accepted answer please? – Bryan Elliott Feb 14 '20 at 18:15
0

Use event listeners to bind the events unobtrusively. Then use event.type to determine what action to take.

//Get the relevent elements
let numText = document.querySelectorAll(".numbertext input[type='text']");

//Loop them
for (var i = 0; i < numText.length; i++) {
  //Now loop the event types we're interested in
  "blur focus mouseenter mouseleave".split(" ").forEach(function(e) {
      //and add the event listener for that event type
      numText[i].addEventListener(e, setInputType);
    })
  }

  //Event listener
  function setInputType(event) {
    var type = "text";
    //Dont change on mouse leave if the element has focus
    if(event.type == "blur" || (event.type == "mouseleave" && this != document.activeElement )) {
      
      this.style.marginLeft = '2ch';
      this.style.width = '4ch';
      type = "text";

    } else {

      this.style.marginLeft = '0';
      this.style.width = '6ch';
      type = "number";

    }
    this.type = type ;
  }
.numbertext {
  color: #f2f2f2;
  font-size: 12px;
  padding: 8px 12px;
  /*position: absolute;
  top: -90px;*/
}
<div class="numbertext">
  <input type="text" style="text-align: right; margin-left: 2ch; width: 4ch;" min="1" max="999" value="1" />/
  <span>999</span>
</div>

<div class="numbertext">
  <input type="text" style="text-align: right; margin-left: 2ch; width: 4ch;" min="1" max="999" value="1" />/
  <span>999</span>
</div>
Jon P
  • 19,442
  • 8
  • 49
  • 72
  • Thank you for your answer that extends the functionality of what I was asking about.. –  Feb 18 '20 at 01:14