我這邊有段 vue code:
<script setup lang="ts">
interface CartItem {
id: any;
itemID: any;
name: string;
price: number;
count: number;
}
let cartItem = ref<CartItem[]>([])
</script>
<template>
<aside>
<div class="cart-item" v-for="(item) in cartItem">
<span v-text="item.name"/>
<span v-text="'$' + item.price"/>
<input type="number" v-model="item.count" />
</div>
</aside>
</template>
這段 code,每次 cartItem 新增一個項目後,就會自動建立一組
<div class="cart-item" />
;反之,每刪除一個項目,就會刪掉一個<div class="cart-item" />
。
我現在要的,是每新增或刪除完一個項目後,自動執行一段 function。
例如新增完畢後,自動 focus 到最新的 input 中。
function(){
let lastItem = document.querySelector('.cart-item input[type=number]') as HTMLInputElement
lastItem.focus()
}
先講原先的錯誤思路
Vue.js 是藉由物件的變化,來新增/刪除
<div class="cart-item" />
,所以我原本是這樣寫
function (){
// 前略……
cartItem.push({
itemID: "cf16c709-6d7f-42f4-b5d6-2aac114c9187",
name: "商品α",
price: 120,
count: 1,
})
let lastItem = document.querySelector('.cart-item input[type=number]') as HTMLInputElement
lastItem.focus()
}
但後來發現這種做法,focus的會是 Vue 建立新的 .cart-item 前,最後一個
<div class="cart-item" />
的
<input type="number">
例如原先是
<aside>
<!-- 1. --> <div class="cart-item"> <!-- 內部中略 --> </div>
<!-- 2. --> <div class="cart-item" />
<!-- 3. --> <div class="cart-item" />
</aside>
執行完這個 function 後,會有 4 個
<div class="cart-item" />
,但 focus 的會是第 3 個
<div class="cart-item" />
的 input
解決辦法
這邊可以用Vue.js 的偵聽器 來解決此問題
watch(
function(){return cartItem.length || 0},
function(newValue, oldValue) {
if(newValue <= oldValue){
return
}
nextTick()
.then(function(){
let lastInput = document.querySelector(".cart-item:last-of-type input[type=number]") as HTMLInputElement
if(lastInput){
lastInput.focus()
}
})
}
)
這段程式碼的意思是,偵測 cartItem 的長度,如果是新增(即 array 長度是增加的情況),繼續執行。
執行 nextTick() ,確保 cartItem 的變動導致的 DOM 變更都處理完畢後,執行我們原先想要的動作——「新增完畢後,自動 focus 到最新的 input 中」
參考資料
Chat GPT、Vue 官網
沒有留言:
張貼留言