Search code examples
javascripthtmlcontenteditableplaceholder

Javascript: Span contentEditable as input (Placeholder Alternative)


I need that when the user deletes the contents of the (span id "ps"), the span id "lbps" is displayed. I need to do this as an alternative to the input placeholder using javascript to this.

<style>
[contenteditable="true"].single-line {
    white-space: nowrap;
    width:200px;
    overflow: hidden;
} 
[contenteditable="true"].single-line br {
    display:none;

}
[contenteditable="true"].single-line * {
    display:inline;
    white-space:nowrap;
}
.noselect {
  -webkit-touch-callout: none;
    -webkit-user-select: none;
     -khtml-user-select: none;
       -moz-user-select: none;
        -ms-user-select: none;
            user-select: none;
}
</style>
<body onload="document.getElementById('ps').focus();document.getElementById('ps').click()">
<span id="ps" onkeypress="document.getElementById('lbps').style.visibility='hidden'" contentEditable="true" class="single-line" spellcheck="false" style="position:absolute;border:1px solid grey;display:inline-block;width:140;height:23;padding-top:4px;padding-left:12px"></span>
<div id="lbps" class="noselect" style="cursor:text;position:relative;padding-top:5px;padding-left:16px;color:lightgrey" spellcheck="false" onclick="document.getElementById('ps').focus();document.getElementById('ps').click()">
Contraseña
</div>
</body>

Solution

  • If you insist to use contenteditable

    To be able to get the value from the span element when the user press a key you would need to use onkeyup event, but this brings some downsides such as lag and bad behavior if holding the key down which can be mitigated by combining onkeydown.

    ☝ Note that I simplified the code to clarify the idea and improve readability.

    var wrapper = document.getElementById('wrapper');
    var password = document.getElementById('password');
    var placeholder = document.getElementById('placeholder');
    
    function onValueChange() {
      placeholder.style.display = password.textContent.length > 0
        ? 'none'
        : 'inline-block';
    }
    
    function setPasswordFocus() {
      password.focus();
    }
    
    window.onload = setPasswordFocus;
    placeholder.onclick = setPasswordFocus;
    
    wrapper.onkeyup = onValueChange;
    wrapper.onkeydown = onValueChange;
    .single-line {
      white-space: nowrap;
      width: 200px;
      overflow: hidden;
    }
    
    .noselect {
      -webkit-touch-callout: none; /* iOS Safari */
        -webkit-user-select: none; /* Safari */
         -khtml-user-select: none; /* Konqueror HTML */
           -moz-user-select: none; /* Old versions of Firefox */
            -ms-user-select: none; /* Internet Explorer/Edge */
                user-select: none; /* Non-prefixed version, currently
                                      supported by Chrome, Edge, Opera and Firefox */
    }
    
    #wrapper {
      position: absolute;
    }
    
    #password {
      position: absolute;
      border: 1px solid grey;
      display: inline-block;
      font-family: arial;
      font-size: 15px;
      width: 140;
      height: 23;
      padding-top: 4px;
      padding-left: 8px;
    }
    
    #placeholder {
      position: relative;
      cursor: text;
      display: inline-block;
      font-family: arial;
      font-size: 15px;
      padding-top: 5px;
      padding-left: 9px;
      color: rgba(0,0,0,.4);
      -webkit-touch-callout: none;
      -webkit-user-select: none;
      -khtml-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
    }
    <div id="wrapper">
      <span id="password" contentEditable="true" class="single-line" spellcheck="false"></span>
      <span id="placeholder" class="noselect">Password</span>
    </div>

    Use input element for much simple and smoother experience

    A contenteditable element is harder to work with than an input element, I would suggest to stick to that so you can use the oninput event to conditionally validate the input value as soon as it is changed.

    You will still be able to use a span element as an alternative to the input placeholder with less struggle.

    ☝ Note that I simplified the code to clarify the idea and improve readability.

    var password = document.getElementById('password');
    var placeholder = document.getElementById('placeholder');
    
    function onValueChange() {
      placeholder.style.display = password.value.length > 0
        ? 'none'
        : 'inline-block';
    }
    
    function setPasswordFocus() {
      password.focus();
    }
    
    window.onload = setPasswordFocus;
    placeholder.onclick = setPasswordFocus;
    
    password.oninput = onValueChange;
    .single-line {
      white-space: nowrap;
      width: 200px;
      overflow: hidden;
    }
    
    .noselect {
      -webkit-touch-callout: none; /* iOS Safari */
        -webkit-user-select: none; /* Safari */
         -khtml-user-select: none; /* Konqueror HTML */
           -moz-user-select: none; /* Old versions of Firefox */
            -ms-user-select: none; /* Internet Explorer/Edge */
                user-select: none; /* Non-prefixed version, currently
                                      supported by Chrome, Edge, Opera and Firefox */
    }
    
    .wrapper {
      position: absolute;
    }
    
    #password {
      position: absolute;
      border: 1px solid grey;
      display: inline-block;
      font-family: arial;
      font-size: 15px;
      width: 140;
      height: 23;
      padding-top: 4px;
      padding-left: 8px;
    }
    
    #placeholder {
      position: relative;
      cursor: text;
      display: inline-block;
      font-family: arial;
      font-size: 15px;
      padding-top: 5px;
      padding-left: 9px;
      color: rgba(0,0,0,.4);
      -webkit-touch-callout: none;
      -webkit-user-select: none;
      -khtml-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
    }
    <div class="wrapper">
      <input id="password" class="single-line" spellcheck="false">
      <span id="placeholder" class="noselect">Password</span>
    </div>