:class的使用及通过:class实现标签切换状态改变

本文介绍了一种基于Vue.js的实现标签切换功能的方法,并展示了如何根据不同的条件改变元素的样式和内容,包括基本的标签切换、点击加载内容、三状态切换及更复杂的状态结合案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

做一个点击标签切换的功能,效果如下:

一、已预设的内容:

HTML:

<ul class="tab">
    <li @click="index=0" :class="{active:index==0}">平面评选</li>
    <li @click="index=1" :class="{active:index==1}">视频评选</li>
    <li @click="index=2" :class="{active:index==2}">音频评选</li>
</ul>
<div class="tabContent">
    <div v-show="index==0">平面评选内容</div>
    <div v-show="index==1">视频评选内容</div>
    <div v-show="index==2">音频评选内容</div>
</div>

JS:

data(){
    return{
      index:0,//默认选中第一个tab标签
    }
  },

当然也可以酱紫写:

HTML:

<ul class="tab">
    <li v-for="(tabs,index) in tabData" @click="current=index" :class="{active:current==index}">{{tabs.name}}</li>
</ul>
<div class="tabContent">
    <div v-for="(content,index) in TabContent" v-show="current==index">{{content.content}}{{content.title}}</div>
</div>

JS:

data(){
    return{
        current:0,//默认选中第一个tab
        tabData:[
            {id:0,name:"平面评选"},
            {id:1,name:"视频评选"},
            {id:2,name:"音频评选"}
        ],
        TabContent:[
            {id:0,content:"平面评选内容",title:"平面组"},
            {id:1,content:"视频评选内容",title:"视频组"},
            {id:2,content:"音频评选内容",title:"音频组"},
        ],
    }
},

二、点击标签加载相应标签的内容:

HTML:

<!--导航部分-->
<ul class="nav">
    <li v-for="(list,index) in navData" :key="index" @click="navChange(index,list.title)" :class="{hover:active==index}"><b>{{list.name}}</b></li>
</ul>


<!--音频和平面视频的样式不一样,通过v-if来控制显示或隐藏-->
<!--平面和视频内容-->
<ul class="List" v-if="PlaneVideoShow">
    <li v-for="(list,id) in listData" :key="id" @click="ToContent(list.orderid)">
        <span>编号:{{list.orderid}}</span>
        <img :src="list.thumb">
        <h6>{{list.tpname}}</h6>
        <p>票数:{{list.num}}</p>
        <button class="bt01">查看详情</button>
    </li>
</ul>


<!--音频内容-->
<ul class="audioList" v-if="AudioShow">
    <li v-for="(list,id) in listData" :key="id" @click="ToContent(list.orderid)">
        {{list.tpname}}
        <p>编号:{{list.orderid}}</p>
    </li>
</ul>


<!--下拉加载更多文字-->
<div class="checkmore" v-show="checkmoreInfo">
<div class="" v-if="page < lastPage"><span>向下滑动加载更多</span></div>
<div class="" v-else><span>没有更多了</span></div>
</div>

CSS:

.nav{display:flex;justify-content:space-around;}
.nav li{font-size:1.6rem;line-height:3.6rem;background:#3d349f;color:#fff;position:relative;}
.nav li.hover{background:#7160e7;}
.nav li b{display:block;padding:0 2rem;}
.nav li:before,.nav li:after,.nav li.hover:before,.nav li.hover:after{content:"";display:block;position:absolute;height:100%;width:1rem;top:0;}
.nav li:before{background:url(images/tagLeft.png) no-repeat left top;background-size:100% 100%;left:-1rem;}
.nav li:after{background:url(images/tagRight.png) no-repeat left top;background-size:100% 100%;right:-1rem;}
.nav li.hover:before{background:url(images/tagHoverLeft.png) no-repeat left top;background-size:100% 100%;left:-1rem;}
.nav li.hover:after{background:url(images/tagHoverRight.png) no-repeat left top;background-size:100% 100%;right:-1rem;}


.checkmore{padding:3rem 0;text-align:center;font-size:1.4rem;}

JS:

data(){
  return{
    loading:true,//加载中(防止ios惯性加载的判断)
    active:0,//默认选中第一个tab
    group:"图片组",//分组参数默认获取数据
    PlaneVideoShow:true,//是否显示视频图片列表
    AudioShow:false,//是否显示音频列表
    navData:[
      {id:0,name:"平面评选",title:"图片组"},
      {id:1,name:"视频评选",title:"视频组"},
      {id:2,name:"音频评选",title:"音频组"}
    ],
    listData:[],//列表数据
    checkmoreInfo:true,//是否显示下滑加载更多文字
    page:1,//当前页码
    lastPage:"",//列表最后一页
    listNum:8,//每页显示数量
  }
},
created(){
  this.getList(); //获取列表数据
},
mounted(){
  //监听页面滚动
  window.addEventListener('scroll',this.windowScroll);
},
methods:{
  //滚动加载更多
  windowScroll(){
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    let clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
    let scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
    if(scrollTop + clientHeight >= scrollHeight && this.page <= this.lastPage && this.loading == false){
      this.loading = true;//ios下拉加载会有惯性请求多次数据,这里通过loading的值来防止多次申请
      this.getList();//申请数据
    }
  },
  //获取列表
  getList(eventName){
    if(eventName != undefined){
      this.group = eventName; //设置group参数为当前点击标签页
    }
  
.......参数提交向后端请求数据........

    if (response.data.status==1) {
      this.listData = this.listData.concat(response.data.data.data);
      this.page = response.data.data.current_page + 1;
      this.lastPage = response.data.data.last_page;
      //console.log("当前页:" + this.page + "最后一页:" + this.lastPage)
      if(eventName == "图片组" || eventName == "视频组"){
        this.PlaneVideoShow = true;
        this.AudioShow = false
      }          
      if(eventName == "音频组"){
        this.PlaneVideoShow = false;
        this.AudioShow = true
      }

 .......此段可用于下拉加载更多数据.......

  },

  //标签切换
  navChange(event,eventName){
    this.active = event;//设置当前标签
    this.page = 1;//重置当前页码
    this.lastPage = "";//清空总页码
    this.listData = [];//清空数据表
    this.getList(eventName);//重新申请当前标签数据
  },
},
watch:{
  listData(){  //监听listData有数据后执行this.$nextTick(() => {})
    var that = this;
    this.$nextTick(() => {
      that.loading = false; ////数据变化时由true改为false,隐藏加载动画,预防ios下滑惯性
    })
  }
},

//离开时注销监听
beforeDestroy () {
  window.removeEventListener('scroll',this.windowScroll);
}, 

三、三状态切换:

一般用于可选/不可选/选中三种情况:

HTML:

<!--UniApp对应HTML-->
<view class="artiflcerTime mt10">
<text 
v-for="(time,index) in timeData" 
:key="index" 
:class="[time.status?'':'active',{actived:isSelect == index}]" @click="getHourTime(index,time.time,time.status)"
>
{{time.time}}
</text>
</view>


<!--Vue对应HTML-->
<div class="artiflcerTime mt10">
<span 
v-for="(time,index) in timeData" 
:key="index" 
:class="[time.status?'':'active',{actived:isSelect == index}]" @click="getHourTime(index,time.time,time.status)"
>
{{time.time}}
</span>
</div>

Css:

/*view和text标签*/
.artiflcerTime text{display:inline-block;background:#fff;box-shadow:0 0 0.5rem #fdd3c6;width:21.7%;text-align:center;margin:0 0 2% 2.5%;padding:0.5rem 0;border-radius:0.5rem;}
.artiflcerTime text.active{background:#f3f3f3;border:0.1rem solid #f3f3f3;color:#969696;}
.artiflcerTime text.actived{background:#fe7831;color:#fff;border:0.1rem solid #fe7831;}


/*div和span标签*/
.artiflcerTime span{display:inline-block;background:#fff;box-shadow:0 0 0.5rem #fdd3c6;width:21.7%;text-align:center;margin:0 0 2% 2.5%;padding:0.5rem 0;border-radius:0.5rem;}
.artiflcerTime span.active{background:#f3f3f3;border:0.1rem solid #f3f3f3;color:#969696;}
.artiflcerTime span.actived{background:#fe7831;color:#fff;border:0.1rem solid #fe7831;}

JS:

<script>
export default {
  data() {
    return {
      isSelect:"none",//时间段选中
      //时间时分数据
      timeData:[
        {id:0,time:"12:00",status:false},
        {id:1,time:"11:00",status:false},
        {id:2,time:"12:00",status:true},
        {id:3,time:"12:00",status:false},
        {id:4,time:"13:00",status:true},
        {id:5,time:"12:00",status:true},
        {id:6,time:"12:00",status:false},
        {id:7,time:"12:00",status:true},
        {id:8,time:"12:30",status:true},
        {id:9,time:"12:10",status:false},
        {id:10,time:"12:00",status:true},
        {id:11,time:"8:00",status:true},
        {id:12,time:"12:00",status:false},
        {id:13,time:"5:30",status:true},
        {id:14,time:"12:00",status:false},
        {id:15,time:"7:10",status:false},
        {id:16,time:"12:00",status:true},
      ],
    }
  },
  methods: {
    //获取时间段
    getHourTime(index,time,status){
      if(status == true){
        this.isSelect = index;
        
        //提交相关数据到服务器
        
       }else{         
        alert("这个时间段不能选!");          
      }
    },
  },
}
</script>

四、更多状态结合:

HTML:

<view class="shadow" v-for="(item,index) in couponItemData" :key="index" 
:class="[
{couponYellow:item.category =='A' && item.status ==0},
{couponGreen:item.category =='B' && item.status ==0},
{couponRed:item.category =='C' && item.status ==0},
{couponGray:item.category != 0}
]" 
@click="couponUsed(item.id)">
<view class="couInfo">
<view class="couInfoMoney"><text>{{item.money}}</text>元</view>
<text class="cffc5aa">{{item.info}}</text>
<text class="couArea">{{item.area}}</text>
</view>
<view class="couTime">
<view class="couHt">{{item.title}}</view>
<view>有效期:{{item.time}}</view>
</view>
<text class="overdue" v-if="item.status == -1">已过期</text>
<text class="noUsed" v-if="item.status == 0">立即使用</text>
<text class="used iconfont" v-if="item.status == 1">&#xe60b;已使用</text>
</view>


CSS:

.couponGray{background:#fff;margin:2rem 0;position:relative;padding:1rem 3rem 1rem 11rem;height:6rem;border-radius:0 0.5rem 0.5rem 0;}
.couInfo{position:absolute;left:0;top:-0.5rem;bottom:-0.5rem;background:#dcdcdc;width:10rem;text-align:center;color:#fff;padding:1rem 0;border-radius:0.5rem;}
.couInfo text{display:block;}
.couInfoMoney text{display:inline-block;font-size:3rem;margin-right:0.3rem;font-weight:bold;}
.cffc5aa{color:#ffc5aa;margin:0.3rem 0;}
.couArea{color:#f1f1f1;}
.couTime{display:flex;flex-wrap:wrap;align-content:space-between;height:100%;color:#969696;}
.couHt{font-size:1.5rem;color:#333;}
.noUsed,.overdue,.used{position:absolute;right:0;top:0;bottom:0;width:1rem;display:flex;align-items:center;border-left:0.1rem dotted #969696;padding:0 1rem;color:#ff7832;font-size:1.3rem;}
.overdue{color:#969696;}
.couponRed .couInfo{background:linear-gradient(to right,#ff8379,#f1383c);}
.couponGreen .couInfo{background:linear-gradient(to right,#14d727,#019e41);}
.couponYellow .couInfo{background:linear-gradient(to right,#ffc044,#ff7832);}
.couponRed .couArea{color:#da0a0a;}
.couponGreen .couArea{color:#086d03;}
.couponYellow .couArea{color:#da510a;}


JS:

data() {
    return {
      //优惠劵数据
      couponItemData:[
        {id:0,money:15,area:"【仅限烟台区域】",title:"【7月会员回馈活动】",time:"2021.7.31",info:"下单直接抵扣",status:0,category:"A"},
        {id:1,money:5,area:"【仅限烟台区域】",title:"【7月会员回馈活动】",time:"2021.7.31",info:"下单直接抵扣",status:1,category:"B"},
        {id:2,money:25,area:"【仅限烟台区域】",title:"【7月会员回馈活动】",time:"2021.7.31",info:"下单直接抵扣",status:-1,category:"C"},
        {id:3,money:10,area:"【仅限烟台区域】",title:"【7月会员回馈活动回馈活动回馈活动回馈活动】",time:"2021.7.31",info:"下单直接抵扣",status:0,category:"A"},
        {id:4,money:5,area:"【仅限烟台区域】",title:"【7月会员回馈活动】",time:"2021.7.31",info:"下单直接抵扣",status:0,category:"B"},
        {id:5,money:25,area:"【仅限烟台区域】",title:"【7月会员回馈活动】",time:"2021.7.31",info:"下单直接抵扣",status:0,category:"C"},
      ],
    }
  },
  methods:{
    //优惠券使用
    couponUsed(id){
      console.log("当前点击id " + id)
    }
  }

附::class的使用(官方文档)

1、:class="变量名",一般是直接绑定一个类名

2、:class = "{ 类名:布尔值 }",类名是否存在或者是否赋值取决于布尔值是true还是false。例如:

<div :class="{hover:isActive}">
我是否显示取决于isActive是true还是false
</div>

<li v-for="(list,index) in navData" 
:key="index" 
@click="navChange(index,list.title)" 
:class="{hover:active==index}">
<b>{{list.name}}</b>
</li>
我是否显示取决于active是否与index的值相等
</li>
<!-- 
:class="{hover:active==index} 
一般都会在data中设置active的值为0与index下标开始值一样
通过navChange方法动态更改active的值与index下标一致
-->

3、:class="[isActive?className:otherName,'defaultName']",默认始终将添加defaultName样式(defaultName需要加引号),可以将defaultName去掉,也可以设置className或otherName其中一个未空,className或otherName取值取决于isActive为true或者false。

<div :class="[isActive ? activeClass : '', 'errorClass']">
默认样式errorClass,但是只有在isActive是true时才添加activeClass
</div>

4、:class="[{hover:isActive},isHover?className:otherName,'defaultName']",在数组语法中也可以使用对象语法 

<div :class="[{hover:isActive},isHover?className:otherName,'defaultName']">
hover的样式取决于isActive是否为true
isHover如果是true为className如果是false则为otherName
defaultName为默认存在样式,也可以为空或者不写(defaultName需要加引号)
</div>

5、:class="[{hover:isActive},'defaultName']",也可以去去掉三元运算。

<div :class="[{hover:isActive},'defaultName']">
hover的样式取决于isActive是否为true
defaultName为默认存在样式,也可以为空或者不写(defaultName需要加引号)
</div>

<think>好的,我现在需要帮用户解决如何使用类(class)来实现收藏图标的状态变化。首先,我得理解用户的需求。他们可能是在开发一个网页应用,比如用HTML、CSS和JavaScript,想要点击收藏图标时切换它的状态,比如从未收藏的空心图标变成实心的已收藏图标,同时可能还要处理相关的业务逻辑,比如发送请求到后端保存状态。 接下来,我应该考虑如何用面向对象的方式来封装这个功能。使用类的好处是可以将状态和行为封装在一起,方便复用和管理。那么,这个类需要哪些属性和方法呢? 属性方面,首先需要一个DOM元素作为图标容器,比如一个div或者button。然后需要两个图标的路径,可能是URL或者base64数据,或者字体图标的类名。另外,还需要当前状态的标志,比如isCollected,布尔值。可能还需要请求的URL,用于保存状态到后端,但用户可能只需要前端部分,所以这个可选。 方法的话,初始化的时候需要绑定点击事件,所以constructor里要处理这些。然后需要一个切换状态的方法toggle,用来改变isCollected的值,并更新图标样式。可能还需要更新图标样式的方法updateIcon,根据isCollected的值切换显示的图标。另外,可能需要一个单独处理点击事件的方法handleClick,里面调用toggle,可能还有发送请求的逻辑。 然后,我需要考虑如何将这两个图标展示出来。如果用图片的话,可能是两个不同的img元素,或者一个元素切换src。如果用字体图标,比如Font Awesome,可能就是切换类名,比如far和fas。这里可能需要不同的处理方式,所以类应该灵活处理这两种情况。 比如,在构造函数里,传入iconElement,这个元素可能是img或者i标签。然后根据类型,切换src或者类名。比如,判断元素是img,就修改src属性;否则,修改类名。 另外,事件监听的添加,应该在类初始化的时候绑定,这样用户只需要创建实例,不需要手动绑定事件。比如,在constructor里,this.iconElement.addEventListener('click', this.handleClick.bind(this))。注意这里的this指向问题,需要用bind来确保方法中的this指向类的实例。 然后,处理异步操作,比如点击后需要发送AJAX请求到后端保存状态。这时候,toggle方法可能需要异步处理,或者提供一个回调函数。或者,在handleClick方法里,先切换状态,再发送请求,如果请求失败再回退状态。这部分需要考虑错误处理,但用户可能只需要一个示例,所以可能先忽略,或者简单处理。 测试一下,比如用户创建一个实例,传入元素和两个图标路径,然后点击时图标切换,并且isCollected状态改变。如果用户需要保存状态到本地存储,也可以加入,但可能超出当前问题的范围。 现在,写一个示例代码。比如HTML中有一个i标签,类名为far fa-heart,表示未收藏。点击后变成fas fa-heart。那么,在JavaScript中,创建一个CollectIcon实例,传入这个元素,未收藏和已收藏时的类名,或者图标路径。这时候,在updateIcon方法里,如果是类名,就替换元素的className,或者使用setAttribute('class', ...)。 可能的代码结构: class CollectIcon { constructor(options) { this.iconElement = options.iconElement; // DOM元素 this.uncollectedIcon = options.uncollectedIcon; // 未收藏时的图标类名或路径 this.collectedIcon = options.collectedIcon; // 已收藏时的图标类名或路径 this.isCollected = options.initialState || false; // 初始状态 this.onChange = options.onChange; // 状态变化时的回调函数 this.init(); } init() { this.iconElement.addEventListener('click', this.handleClick.bind(this)); this.updateIcon(); } handleClick() { this.toggle(); if (this.onChange) { this.onChange(this.isCollected); } // 这里可以添加发送请求的逻辑 } toggle() { this.isCollected = !this.isCollected; this.updateIcon(); } updateIcon() { if (this.iconElement.tagName === 'IMG') { this.iconElement.src = this.isCollected ? this.collectedIcon : this.uncollectedIcon; } else { // 假设使用字体图标,切换类名 const iconClass = this.isCollected ? this.collectedIcon : this.uncollectedIcon; this.iconElement.className = iconClass; } } } // 使用示例: const heartIcon = document.querySelector('#heart-icon'); const collector = new CollectIcon({ iconElement: heartIcon, uncollectedIcon: 'far fa-heart', collectedIcon: 'fas fa-heart', initialState: false, onChange: (state) => { console.log('当前状态:', state ? '已收藏' : '未收藏'); } }); 这样,用户就可以通过创建CollectIcon实例来管理不同的收藏图标。同时,onChange回调可以处理状态变化后的逻辑,比如发送AJAX请求。 需要注意的地方: 1. 如果用户使用图片,需要处理src的切换;如果是字体图标,处理类名。可能需要更灵活的处理方式,比如允许用户自定义如何更新图标,比如传入一个函数。 2. 事件监听器的移除,如果实例需要销毁,应该提供destroy方法,但可能用户暂时不需要,所以先不考虑。 3. 初始状态的同步,比如从服务器获取当前是否已收藏,需要在初始化时设置initialState为正确的值。 4. 错误处理,比如图标路径不存在,或者类名错误,但这是用户的责任,类不处理这些。 可能用户的问题还有,如何与后端交互,比如在handleClick里发送请求,并处理可能的错误。比如,在toggle之前发送请求,如果成功再切换状态,否则不切换。这时候需要将toggle改为异步,或者在发送请求成功后才改变状态。 比如修改handleClick为异步函数: async handleClick() { try { const newState = !this.isCollected; // 假设saveStateToServer返回一个Promise await saveStateToServer(newState); this.isCollected = newState; this.updateIcon(); if (this.onChange) { this.onChange(this.isCollected); } } catch (error) { console.error('保存状态失败:', error); } } 这样,在请求成功后才更新状态。但这样需要用户自己实现saveStateToServer函数,或者作为配置项传入。 这可能让类更复杂,但用户可能需要这样的示例。所以可能需要调整类设计,允许用户传入一个保存状态的方法,或者在onChange中处理。 综合考虑,用户可能希望类主要负责UI状态切换,而业务逻辑由回调处理。因此,示例中的onChange回调已经足够,用户可以在其中处理发送请求的逻辑,如果请求失败,可以手动回滚状态。 例如: onChange: (state) => { api.saveCollect(state).then(() => { // 成功 }).catch(() => { // 失败,回滚状态 collector.toggle(); // 再次切换状态 }); } 但这样需要访问collector实例,可能需要在回调中传递实例,或者使用其他方式。这可能稍微复杂,但作为示例,可能已经足够。 总结,类的主要职责是管理图标的状态和显示,业务逻辑通过回调处理。这样设计比较合理,职责分离,类更通用。</think>以下是使用 JavaScript 类(Class实现收藏图标状态变化的示例,包含详细注释和代码说明: ### 实现思路 1. **封装性**:用类将图标状态、DOM操作和点击事件封装在一起。 2. **状态管理**:通过 `isCollected` 属性记录当前状态。 3. **UI同步**:根据状态切换不同样式的图标(支持图片或字体图标)。 4. **扩展性**:提供 `onChange` 回调函数处理业务逻辑(如发送请求)。 --- ### 完整代码示例 ```html <!-- HTML部分 --> <i id="collect-icon" class="far fa-heart"></i> ``` ```javascript // JavaScript 类实现 class CollectIcon { constructor(options) { // 绑定DOM元素 this.iconElement = options.iconElement; // 未收藏/已收藏的图标样式(支持图片路径或字体图标类名) this.uncollectedIcon = options.uncollectedIcon; this.collectedIcon = options.collectedIcon; // 当前状态(默认未收藏) this.isCollected = options.initialState || false; // 状态变化回调
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值