今天寫項目時遇到了一個坑,情景描述如下:
用v-for創建了8個li,每個li都有一個類按鈕(下文就稱之為按鈕),點擊該按鈕時,e.target的背景圖片發生變化,再點擊該按鈕e.target的背景圖片又恢復成初始狀態,如此循環。
未點擊時效果圖:
點擊后:
如果只是用js,很容易解決,可在vue組件中并非如此了。
開始的時候,只寫了一個控件來切換背景圖片,但是類似于this.item=!this.item表達式并不能如愿切換。
然后就寫了兩個控件,不同控件不同背景圖,在設置樣式時讓他們重疊,通過v-show指令來決定顯示哪個控件。在這里,v-show監控的值也是變化的。這樣對用戶來說,“還是一個按鈕”。然而結果卻是,數據如愿改變,dom元素的樣式并沒有隨之變化。具體就是第二個控件顯示的v-show的值始終不變。無論怎么修改數據(data,computed...),我自巋然不變,崩潰啊。
貼代碼吧:
html部分:
<ul class="selectmenu">
<li v-for="(item,index) in createAccount.menu" class="col-md-3 col-xs-6" :key="index" :v-model="index">
<div class="item">
<div class="pic">
<img :src="'image/account/'+item.img" alt="">
<span v-show="unchecked[index]" :data-idx="index" class="selected" :style="selectStyle" @click="changeStyle()"></span>
<span v-show="unchecked[index]" :data-idx="index" class="selected" :style="selectedStyle" @click="changeStyle1()"></span>
</div>
<p>{{item.text}}</p>
<div v-show="item.ishelp" class="help">
<span>?</span>
<p v-show="item.helptext" class="helptext">{{item.helptext}}</p>
</div>
</div>
</li>
</ul>
js部分:
var CreateAccount={
template:"#tpl_createAccount",
data:function(){
return {
createAccount:{},
selectStyle:{
backgroundImage:'url(../image/account/icon_checkmark.png)',
backgroundPosition:'center center',
backgroundSize:'auto 100%',
backgroundRepeat:'no-repeat'
},
selectedStyle:{
backgroundImage:'url(../image/account/icon_checkmark_selected.png)',
backgroundPosition:'center center',
backgroundSize:'auto 100%',
backgroundRepeat:'no-repeat'
}
}
},
computed:{
unchecked:function(){
return [true,true,true,true,true,true,true,true]
},
checked:function(){
return [false,false,false,false,false,false,false,false]
}
},
created:function(){
this.$http.get("data/createaccount.json")
.then(function(res){
this.createAccount=res.data.data;
});
},
methods:{
changeStyle:function(e){
var e=event||window.event;
var tar=e.target.getAttribute("data-idx");
Vue.set(this.unchecked,tar,false);
Vue.set(this.checked,tar,true);
},
changeStyle1:function(e){
var e=event||window.event;
var tar=e.target.getAttribute("data-idx");
Vue.set(this.unchecked,tar,true);
Vue.set(this.checked,tar,false);
}
}
};
css就省略了。
絞盡腦汁,想到v-show的本質就是通過設置display:none實現元素的顯隱,盡然這里v-show沒辦法做到,那就直接控制元素的display值吧。
于是改變代碼:
html去掉v-show這一部分,如下:
<ul class="selectmenu">
<li v-for="(item,index) in createAccount.menu" class="col-md-3 col-xs-6" :key="index" :v-model="index">
<div class="item">
<div class="pic">
<img :src="'image/account/'+item.img" alt="">
<span :data-idx="index" class="selected" :style="selectStyle" @click="changeStyle()"></span>
<span :data-idx="index" class="selected" :style="selectedStyle" @click="changeStyle1()"></span>
</div>
<p>{{item.text}}</p>
<div v-show="item.ishelp" class="help">
<span>?</span>
<p v-show="item.helptext" class="helptext">{{item.helptext}}</p>
</div>
</div>
</li>
</ul>
js則在樣式里添加上display,如下:
var CreateAccount={
template:"#tpl_createAccount",
data:function(){
return {
createAccount:{},
selectStyle:{
display:"block",
backgroundImage:'url(../image/account/icon_checkmark.png)',
backgroundPosition:'center center',
backgroundSize:'auto 100%',
backgroundRepeat:'no-repeat'
},
selectedStyle:{
display:"none",
backgroundImage:'url(../image/account/icon_checkmark_selected.png)',
backgroundPosition:'center center',
backgroundSize:'auto 100%',
backgroundRepeat:'no-repeat'
}
}
},
computed:{
unchecked:function(){
return [true,true,true,true,true,true,true,true]
},
checked:function(){
return [false,false,false,false,false,false,false,false]
}
},
created:function(){
this.$http.get("data/createaccount.json")
.then(function(res){
this.createAccount=res.data.data;
});
},
methods:{
changeStyle:function(e){
var e=event||window.event;
var tar=e.target.getAttribute("data-idx");
Vue.set(this.unchecked,tar,false);
Vue.set(this.checked,tar,true);
this.selectStyle.display="none";
},
changeStyle1:function(e){
var e=event||window.event;
var tar=e.target.getAttribute("data-idx");
Vue.set(this.unchecked,tar,true);
Vue.set(this.checked,tar,false);
this.selectStyle.display="block";
}
}
};
問題解決了嗎?——————答案是:沒-----有!
先不管樣式是否完整,首先上述js代碼中this.selectStyle依舊是組件中的全局變量,也就是會影響到所有的li里面的按鈕,而并非僅僅是e.target。
再想辦法吧!要改變的僅僅是e.target的。
直接設置e.target.style的值?當然啦,然而這里style要提前定義一下,不然會提醒你style未定義。
然后因為是兩個span,一個是e.target,一個是e.target的替身(它的兄弟元素),需要把這個替身獲取到。
中間過程省略,太傷心。
后來只是修改了js部分:
var CreateAccount={
template:"#tpl_createAccount",
data:function(){
return {
style:null,
createAccount:{},
selectStyle:{
display:"block",
backgroundImage:'url(../image/account/icon_checkmark.png)',
backgroundPosition:'center center',
backgroundSize:'auto 100%',
backgroundRepeat:'no-repeat'
},
selectedStyle:{
display:"none",
backgroundImage:'url(../image/account/icon_checkmark_selected.png)',
backgroundPosition:'center center',
backgroundSize:'auto 100%',
backgroundRepeat:'no-repeat'
}
}
},
computed:{
unchecked:function(){
return [true,true,true,true,true,true,true,true]
},
checked:function(){
return [false,false,false,false,false,false,false,false]
}
},
created:function(){
this.$http.get("data/createaccount.json")
.then(function(res){
// console.log(res.data);
this.createAccount=res.data.data;
});
},
methods:{
changeStyle:function(e){
var e=event||window.event;
var el=e.target;
var tar=e.target.getAttribute("data-idx");
var next=$(el).next()[0];
Vue.set(this.unchecked,tar,false);
Vue.set(this.checked,tar,true);
e.target.style=this.selectStyle;
e.target.style.display="none";
next.style=this.selectedStyle;
next.style.display="block";
next.style.backgroundImage='url(../image/account/icon_checkmark_selected.png)',
next.style.backgroundPosition='center center',
next.style.backgroundSize='auto 100%',
next.style.backgroundRepeat='no-repeat'
this.selectStyle=next.style;
// this.selectedStyle.display="block";
},
changeStyle1:function(e){
var e=event||window.event;
var el=e.target;
var tar=e.target.getAttribute("data-idx");
var pre=$(el).prev()[0];
Vue.set(this.unchecked,tar,true);
Vue.set(this.checked,tar,false);
e.target.style=this.selectedStyle;
pre.style.display="block";
pre.style.backgroundImage='url(../image/account/icon_checkmark.png)',
pre.style.backgroundPosition='center center',
pre.style.backgroundSize='auto 100%',
pre.style.backgroundRepeat='no-repeat'
this.selectedStyle=pre.style;
}
}
};
很麻煩吧!我也覺得。曾嘗試著刪除一部分看似能刪除的代碼,結果是不能刪。
你以為這樣問題就解決了嗎?只是解決了一部分。
第一次點擊和第二次點擊都沒有問題(忽略那個報錯吧,還木有想到辦法避免,重要的是想要的結果出來了),但是第三次第四次點擊的時候就沒有效果啦。。。。。。問題出在哪里呢?就是第三次點擊的時候e.target沒有如愿變成第一個span,而且第一個span沒有了樣式。
....好吧,我能說在發布完文章后,我又去修改了一下代碼,僅僅只是加了一行代碼而已,問題就解決了嗎?淚崩!
changeStyle1:function(e){
var e=event||window.event;
var el=e.target;
var tar=e.target.getAttribute("data-idx");
var pre=$(el).prev()[0];
Vue.set(this.unchecked,tar,true);
Vue.set(this.checked,tar,false);
e.target.style=this.selectStyle;
e.target.style.display="none";
pre.style=this.selectedStyle;
pre.style.display="block";
pre.style.backgroundImage='url(../image/account/icon_checkmark.png)',
pre.style.backgroundPosition='center center',
pre.style.backgroundSize='auto 100%',
pre.style.backgroundRepeat='no-repeat'
}
就是那行e.target.style.display="none";
不過呢,還是覺得很麻煩,這些代碼基本都是缺什么就添加什么添出來的。有沒有小伙伴有更簡單的辦法呢?求分享咯!
————————————————————————————————
后來的優化:
http://www.lxweimin.com/p/4fe1f4d90a5d