Мир стоит вверх ногами. Корпорация добра печется о нашей безопасности. И в то же время делает все, чтобы наши секреты стали достоянием гласности. Это я о политике гугл в плане сохранения логин-паролей в браузере, через него в аккаунте и о доступности всего нашего секретного добра всем, кто сможет подойти к нашим компьютерам.

Да, удобно. Но небезопасно. И временами очень вредно.

Запустил я очередной проект на работе. И самой большой проблемой по опыту старого проекта было то, что сотрудники вовсю пользовались автосохраненим паролей, поэтому под Васей мог сидеть Сергей и ни о какой нормальной аутентификации, а тем более разраничении прав доступа речи идти не могло.

Сначала двинулся в сторону того же гугла — как отключить или запретить автосохранение или автоподстановку сохраненного пароля? Старые рецепты с autocomplete="off" и autocomplete="new-password" уже не работают, последний работающий рецепт оказался прост — головную боль следует лечить усекновением болящей. То есть глобальным отключением автосохранения. Но это не вариант. Пусть юзеры дальше балуются своими паролями от почты, соцсетей и прочих радостей жизни, мне не жалко.

Далее последовал легкий инжиниринг. И вот какой результат — если на страничке есть input type="password", то при появлении первого символа в адресной строке появляется ключик. И все. Что ты ни делай, какие html тэги ни прописывай — есть password, значит, надо бежать и барину кланяться, предлагая все мыслимые и немыслимые блага от того, что барин позволит сохранить пароль. Ну и потом барин будет весьма доволен, что пароль не надо вспоминать, а просто его вставить.

Хмм... подумал я... И решил — раз у хрома такая нервная реакция на type="password", то надо просто от него отказаться и всех делов то!

Сказано — сделано. Изваял компонент, который незамысловато делает простейшую вещь — на экран выводит точечки, а в v-model передает то, что введено. Ну и плюсом отрабатывает клавишу Enter.

Конечно, не обошлось без ограничений. Но они очень малы и незначительны — нельзя курсор перемещать стрелками, только backspace. Но, учитывая, что под точками и так не видно, где и что надо подправить — вероятность испытать колоссальное неудобство от такого ограничения стремится к нулю. Не стал делать кнопочку с глазиком, чтобы посмотреть, что же я ввел, label для input оформил в стиле компонента vuetify, то есть текст-подсказка уползает вверх при фокусе и возвращается при утере фокуса при условии, что ничего не было введено.

Ну и использование такого компонента тоже очень простое — создать .vue файл, импортировать в проект и использовать в шаблоне.

Теперь, собственно, код.

Код под спойлером
<template>
    <div class="logpass">
        <input type="hidden" v-model="opass">
        <label :for="inpid">Пароль</label>
        <input style="width: 100%"
               :id="inpid"
               type="text"
               v-model="ipass"
               @input="input"
               @keydown="keyDown"
               @focus="focus"
               @blur="blur"
               @keydown.enter="$emit('keydown_enter',$event)">
    </div>
</template>

<script>
    export default {
        name: "login-password",
        data() {
            return {
                ipass:'', // input password
                opass:'', // output password
            }
        },
        computed: {
            inpid(){
                let length=7
                let result
                let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
                let letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
                let el=''
                while(el!==null){ // void to duplicate id if node with same id exists
                    result=letters.charAt(Math.floor(Math.random() *
                        letters.length));
                    for (let i = 0; i < length; i++) {
                        result += characters.charAt(Math.floor(Math.random() *
                            characters.length));
                    }
                    el=document.querySelector('#'+result)
                }
                return result;
            },
        },
        methods: {
            focus(event){
                let el=document.querySelector(['label[for="'+event.target.id+'"]'])
                if(el){
                    el.classList.add('label-active')
                }
            },
            blur(event){
                let el=document.querySelector(['label[for="'+event.target.id+'"]'])
                if(el && this.opass==''){
                    el.classList.remove('label-active')
                }
            },
            input(event){
                if(this.opass.length<this.ipass.length){
                    let s = this.ipass.slice(-1)
                    this.opass=''.concat(this.opass,s)
                    s = ''
                    for(let i=0;i<this.ipass.length;i++)s=''.concat(s,'•')
                    this.ipass=s
                    this.$emit('input',this.opass)
                }else{
                    if(this.opass.length>this.ipass.length){
                        this.opass=this.opass.slice(0,this.ipass.length)
                        this.$emit('input',this.opass)
                    }
                }
            },
            keyDown(event){
                if(event.key=='ArrowLeft'||event.key=='ArrowRight'||event.key=='ArrowUp'||event.key=='ArrowDown'){
                    event.preventDefault()
                }
            },
        },
    }
</script>

<style scoped lang="scss">
.logpass{
    padding-top:14px;
    padding-bottom: 12px;
    display: flex;
    & label{
        padding: 5px;
        position: absolute;
        color: #999;
        transition: .3s cubic-bezier(.25,.8,.5,1);
    }
    & .label-active{
        font-size: 10px;
        margin-top: -20px;
        margin-left: -5px;
    }
}
</style>

Как видим — достаточно простая логика — при вводе очередного символа он заменяется на точку и дублируется в переменную, переданную в v-model. Именно запрет на перемещение курсора в поле ввода позволил реализацию такой логики.

Еще одно замечание — этот компонент не использует инициализацию значением, которое, возможно уже было в v-model-переменной.

Пример использования:

// какой-нибудь наш модуль на vue, в котором необходим ввод пароля
<template>
	<div>
    логин: <input type="text" v-model="mylogin">
    <login-password v-model="mypassword" @keydown_enter="makeLogin">
    </login-password>
  </div>
</template>
<script>
    export default {
        name: "Login",
        data() {
            return {
                mylogin: '',
                mypassword:''
            }
        },
        components: {
            LoginPassword:()=>import('./login-password')
        },      	
      	methods:{
          makeLogn(){
            .....
          }
          .......
        

              

Ну и напоследок — код свободный, берите, кто хотите, возможно, кому-то идея подойдет на React или Angular.