简体   繁体   English

如何在 JavaScript/Vue 的文本字段中获取插入符号的当前 position?

[英]How do I get the current position of the caret in a text-field in JavaScript/Vue?

I'm attempting to implement a small quality of life change to a text field to allow users to change the dollar amount of a text field if the cents are full.我正在尝试对文本字段进行少量的生活质量更改,以允许用户在美分已满时更改文本字段的美元金额。

Currently, my fields will append a decimal amount based on what has been entered when the user moves onto another field (triggering an @blur event).目前,我的字段将 append 基于用户移动到另一个字段时输入的内容(触发@blur事件)的十进制数量。

  • 1 will become 1.00 1将变为1.00
  • 1. will become 1.00 1.将变为1.00
  • 1.5 will become 1.50 1.5将变为1.50

The way I have it coded is to check if the field matches a regex ( /((\d*\.){1}(\d{2}))/ ) to see if the field is "full".我对其进行编码的方式是检查该字段是否与正则表达式匹配( /((\d*\.){1}(\d{2}))/ )以查看该字段是否“已满”。 If so, I want to get the user's caret/cursor/insertion point/whatever term it is to check and see if it's before or after the decimal point.如果是这样,我想获取用户的插入符号/光标/插入点/无论是什么术语,以检查它是在小数点之前还是之后。

  • If it's before the decimal point, ie 100|.00 , I want them to be able to enter more numbers.如果它在小数点之前,即100|.00 ,我希望他们能够输入更多数字。
  • If it's after the decimal point, ie 100.|00 , I don't want them to be able to enter more numbers.如果它在小数点之后,即100.|00 ,我希望他们能够输入更多的数字。

This sounds like a job for refs and selectionStart/selectionEnd , and you'd almost be right (and you probably still are).这听起来像是refsselectionStart/selectionEnd的工作,你几乎是对的(而且你可能仍然是)。 Even after Google removed that functionality for fields with type="number" , there was a work around using those properties.即使在 Google 删除了type="number"字段的该功能之后,也有使用这些属性的解决方法。 I've seen many different posts suggesting that, some dating back almost 13 years.我看过许多不同的帖子,其中一些可以追溯到近 13 年前。 I just can't seem to get it to work right though.不过,我似乎无法让它正常工作。

Here's what I have:这是我所拥有的:


Template tag模板标签

<v-container>
    <v-row v-for="(exp, index) in travelExpenseArray" :key="exp.id" style="margin-top: -20px;" >
        <v-col v-show="$vuetify.breakpoint.smAndUp" cols="auto" sm="2" />
        <v-col cols="auto" sm="4" >
            <v-checkbox  v-model="exp.checked" :label="exp.title" dense @change="checkEnabled(index, exp.checked)"/>
        </v-col>
        <v-col cols="auto" sm="4">
            <v-card-text style="padding: 0px;">{{exp.title}} Expenses</v-card-text>
            <v-text-field :ref="`vtf${index}`" outlined step=".01" v-model="exp.expense" type="number" hide-details
            dense single-line :disabled="!exp.checked" hide-spin-buttons @keypress="checkValidKey($event, index)" 
            @click="clearCurrencyField(index)" @blur="appendZeros(index)"/>
        </v-col>
        <v-col v-show="$vuetify.breakpoint.smAndUp" cols="auto" sm="2" />
    </v-row>

    <v-row style="margin-top: -20px;">
        <v-col cols="auto" sm="6" />
        <v-col cols="auto" sm="4">
            <v-card-text style="padding: 0px">Total Travel Expenses</v-card-text>
            <v-text-field outlined v-model="totalTravelExpenses" dense disabled />
        </v-col>
        <v-col v-show="$vuetify.breakpoint.smAndUp" cols="auto" sm="2" />
    </v-row>
</v-container>

To break it down: I have a travel expense array that I loop over and dynamically create text fields for, v-model -ing the values from the array to the ones from the text fields.分解它:我有一个旅行费用数组,我循环并动态创建文本字段, v-model将数组中的值与文本字段中的值相匹配。 I'm fortunately able to create dynamic ref='' attributes, which will come in handy in the script section.幸运的是,我能够创建动态ref=''属性,这将在脚本部分派上用场。


Script Tag脚本标签

(specifically, the checkValidKey method, where this is happening) (特别是发生这种情况的 checkValidKey 方法)

checkValidKey(evt, index){
    let expense = this.travelExpenseArray[index].expense;               
    // Get the key code from the event.
    const charCode = evt.which ? evt.which : evt.keyCode;

    // Ensure key is a valid key ('0-9.' and numpad '0-9.').
    // Prevent if there's no match.
    if(!this.getValidKeys().includes(charCode)){
        evt.preventDefault();

    // okay so this block checks the expense against the regex
    // if it matches, (tentatively) prevent the key press 
    // because a 'full' cost has been entered.
    }else if((/((\d*\.){1}(\d{2}))/).test(expense)){

        // chrome removed this functionality for `type = "number"` fields in 2014 or so
        this.$refs[`vtf${index}`][0].$refs.input.type = "text";
        console.log(this.$refs[`vtf${index}`][0].$refs.input.selectionStart);
        // not typing all that out over and over
        let selection = {
            start: this.$refs[`vtf${index}`][0].$refs.input.selectionStart,
            end: this.$refs[`vtf${index}`][0].$refs.input.selectionEnd
        }
                    
        // check if the cursor is in a valid position
        if(selection.start >= selection.end - 2){
            evt.preventDefault();
        }
        // should probably do this to be safe; this seems like
        // a really hacky solution to such a negligible problem
        this.$refs[`vtf${index}`][0].$refs.input.type = "number";
                    
        return;
    }
},

The comments say the same thing but first I get the value of the key pressed.评论说同样的话,但首先我得到按下的键的值。 If it's not in a list of valid keys, ignore the press.如果它不在有效键列表中,请忽略按下。 If it is, and the value of the field matches the regex, I get the field by it's $ref with a little bit of black magic that I feel shouldn't work, but consider myself fortunate that it does.如果是,并且该字段的值与正则表达式匹配,我会通过它的$ref获得该字段,并带有一点我觉得不应该工作的黑魔法,但我认为自己很幸运,它确实如此。 I convert the type to text to allow me to use selectionStart and selectionEnd , and I then take what are supposed to be the start and end points and store them in an object to do a little bit of shorthand.我将类型转换为文本以允许我使用selectionStartselectionEnd ,然后我将应该是起点和终点的内容存储在 object 中以做一些速记。 I do a comparison, and if it passes, I prevent the key from registering.我做了一个比较,如果它通过了,我会阻止密钥注册。 Then I convert the type of field back to number and return.然后我将字段类型转换回数字并返回。

Also, here's how the expenses are structured in the vuex store, in case that's useful:另外,如果有用的话,这里是费用在 vuex 商店中的结构:

travelExpenseArray: [
    {id: 0, title: 'Bus', checked: false, expense: '0.00'},
    {id: 1, title: 'Car', checked: false, expense: '0.00'},
    {id: 2, title: 'Plane', checked: false, expense: '0.00'},
    {id: 3, title: 'Train', checked: false, expense: '0.00'},
],

The issue I'm running into is that for some reason, selectionStart and selectionEnd are both coming up as 0 .我遇到的问题是,由于某种原因, selectionStartselectionEnd都出现为0 I understand that this is likely because no text is actually selected , but there's no getCaretPosition method or caretPosition property.我知道这可能是因为实际上没有选择任何文本,但是没有getCaretPosition方法或caretPosition属性。 I would really love for this workaround to... work, but I also don't want to keep wasting time hoping it does when it won't.我真的很喜欢这种解决方法......工作,但我也不想继续浪费时间希望它不会这样做。

So what I was doing was easily accomplished (and more) by just installing a library instead of reinventing the wheel.所以我所做的事情很容易完成(甚至更多),只需安装一个库而不是重新发明轮子。

I just installed v-money with npm install v-money .我刚刚用npm install v-money v-money

App.vue应用程序.vue


Added the following lines:添加了以下几行:

import money from 'v-money';
Vue.use(money);

TravelExpenseForm.vue TravelExpenseForm.vue

(the component that uses this method) (使用此方法的组件)


Modified the v-text-field to include the v-money="money" directive.修改了v-text-field以包含v-money="money"指令。

Added the following object to the data option:在数据选项中添加了以下 object:

money: {
    decimal: '.',
    thousands: ',',
    prefix: '$',
    suffix: '',
    precision: 2
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM