How to make input type="tel" work as type="password"

There are several ways you can accomplish this:

1) -webkit-text-security

The most elegant solution is to use the builtin -webkit-text-security: circle; CSS style which is intended for this very thing. Unfortunately that is not portable, and isn't supported on IE or Edge. You can find a workaround to get support on other browsers here if you choose to use this approach.

2) Changing the type to password after it is selected

As Fidel90 and others have pointed out, you could try switching the type="tel" to type="password" after the user has selected it. I made a fiddle of that idea here. It works perfectly in iOS, but in Android it fails to launch the first tap, and then launches as the default keyboard on the second tap.

3) Using a font that has only one character

Another idea suggested by rybo111 in the comments which has been implemented by noppa below is to use a font that has only a password dot character. This is likely the most portable and least hackish solution. One drawback is that the user cannot see the character they just typed as is the usual behaviour for modern mobile password fields. A comprehensive fiddle of this is posted here.

4) Copy the text into a hidden input as the user types

My idea (below) is to use a second input to store the real number and then hide the number as the user types it. Note that this will filter all non-phone number characters from the final number, though you can modify the variable to accept whatever you want.

Tested and works on:

  • iOS 9 Safari
  • Android 6.0 Chrome
  • Internet Explorer 11
  • FireFox 45
  • Chrome 50
  • Opera 36
  • Microsoft Edge 25

var BACKSPACE_CHARS = [8, 46, 229];

function hideNumber(fakeInput, event) {
    var hideChar = '*';
    // add characters that you want to appear in the final number to this
    // string -- leave the string empty to allow all characters
    var phoneChars = '0123456789()-+';
    var keyCode = event.keyCode || event.charCode;
    var key = String.fromCharCode(keyCode)+'';
    var realInput = document.getElementById("hidden-number");
    var len = fakeInput.value.length;
    fakeInput.value = '';
    if(phoneChars.indexOf(key) > -1 || !phoneChars.length) {
        realInput.value += key;
    } else {
        if(BACKSPACE_CHARS.indexOf(keyCode) < 0) {
            --len;
        }
    }
    for(var i=0; i<len; i++) { // no String.repeat() in IE :(
        fakeInput.value += hideChar;
    }
    updateDisplay();
}

function backspace(event) {
    var keyCode = event.keyCode || event.charCode;
    var realInput = document.getElementById("hidden-number");
    if(BACKSPACE_CHARS.indexOf(keyCode) > -1) { // backspace or delete
        var len = realInput.value.length;
        realInput.value = realInput.value.slice(0, len-1);
    } 
    updateDisplay();
}

function updateDisplay() {
    var realInput = document.getElementById("hidden-number");
    var display = document.getElementById("display");
    display.innerHTML = realInput.value || '';
}
<input type="tel" name="number" id="number-hider" onkeypress="hideNumber(this, event)" onkeydown="backspace(event)" onblur="hideNumber(this)">
<input type="hidden" name="realnumber" id="hidden-number">
<div id="display"></div>

Known bugs:

  • If a user taps or clicks in the middle of the text box and types or backspaces, the characters will be added or deleted from the end of the real number.

Update 2020-06-19

If all you're after is better usability for "pin" inputs, I'd now suggest sticking with the inputmode attribute that didn't work for OP in 2016. The browser support is still quite limited, but it's finally working at least on iOS Safari and as a solution it's much simpler and possibly more secure than avoiding input type="password" altoghether.

<input type="password" inputmode="tel" />

So for OP's specific use-case I'd now recommend the above even though browser support is not 100%. I know some people coming to this post are actually looking for type="password" alternatives for other reasons, like to circumvent password autocomplete, and my original answer below may still help them.

Also note that my password font project that I recommend below has had a few updates since I gave this answer and has a bit different installation instructions now.


Original answer

I know this already has a nice, working and accepted solution from jdgregson, but I really liked rybo111's idea of a custom font based solution and wanted to give it a try.

The idea is to create a font that only contains circles so that when the font family is applied to the input element, seemingly no characters are displayed.

So in case anyone's still interested in a non-JS solution with decent browser support and without the known issues with jdgregson's answer, I created a simple font for this.

GitHub repo: https://github.com/noppa/text-security

JSFiddle demo: https://jsfiddle.net/449Lamue/6/

The font can be used by including the dist/text-security.css either from cloned repo or from RawGit or something similar. After including the css you can use the font by setting the element's font-family to 'text-security-disc'.

<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/noppa/text-security/master/dist/text-security.css">
<style>
     input [type="tel"] {
         font-family: "text-security-disc";
         -webkit-text-security: disc;
     }
</style>

In the spirit of "polyfilling" the -webkit-text-security, I also added the "circle" and "square" options to use as the hiding symbol.

I've tested this with Chrome v49, IE11, Microsoft Edge 25 and Chrome v50 on Android 5.0, in which the input of type "tel" opens the number keypad.