Vue.js - 改变除被点击元素外的所有元素的颜色

4
我正试图从jQuery转换到Vue.js,但卡住了。页面上有三个按钮。当我点击一个按钮时,我希望所有其他按钮的背景颜色变为绿色,并将被点击的按钮的颜色更改为黑色。
使用jQuery只需要两行代码,但我无法弄清如何在Vue.js中实现。此外,Vue.js似乎没有this关键字。
同时,我想直接应用原始的CSS background-color属性,而不是应用类。
以下是我的jQuery代码 - 非常简单。
<div class="main-content-area">    
  <div class="btn">Click me!</div>
  <div class="btn">Click me!</div>
  <div class="btn">Click me!</div>    
</div>

const Example = new Vue({
  el: '.main-content-area',
  methods: {
    addEventListeners() {
      $(document).ready(function() {
        $(".btn").click(function() {
          $(".btn").css("background-color", "green"); // Make all buttons green
          $(this).css("background-color", "black"); // Make the clicked button black
        });
      });
    }
  },
  mounted: function() {
    this.addEventListeners();
  }
})

使用Vue.js - 我只做到了这一步...

<div class="main-content-area">    
  <div class="btn" @click="changeColor">Click me!</div>
  <div class="btn" @click="changeColor">Click me!</div>
  <div class="btn" @click="changeColor">Click me!</div>    
</div>

const Example = new Vue({
  el: '.main-content-area',
  methods: {
    changeColor() {
      //  Change color to green for all .btn elements
      //  and change  color for clicked .btn to black
    }
  })
2个回答

1

使用组件来创建按钮:

HTML代码:

<div class="main-content-area">

    <my-custom-button component-type="my-custom-button" ></my-custom-button>
    <my-custom-button component-type="my-custom-button"></my-custom-button>
    <my-custom-button component-type="my-custom-button"></my-custom-button>

</div>

JavaScript:

Vue.component("my-custom-button",{
        template : '<div class="btn" :style="buttonStyles" @click="activeThisButton" >Click me!</div>',

    data(){
        return {
        isActive : false,
      }
    },

    computed : {
        buttonStyles(){
        return {
            backgroundColor : this.isActive  ? 'green' : '',
        }
      }
    },

    methods : {
        activeThisButton(){
        // call inactiveAllButtons on parent to deselect all buttons
        this.$root.inactiveAllButtons();
        // active current button
        this.isActive = true;
      }
    }
})

const Example = new Vue
({

    el: '.main-content-area',

    methods : {
        // filter children and find buttons ( by component-type property )
        // and inactive all .
        inactiveAllButtons(){
        var buttons = this.$children.filter(function(child) {
            return child.$attrs['component-type'] === 'my-custom-button';
        });
        for(var i = 0 ; i < buttons.length ; i++ ){
          buttons[i].isActive = false;
        }
      }
    }

});

jsfiddle演示


当单击一个按钮时,它不会取消选择所有其他按钮。 - Liga
这是一种非常糟糕的方法。在这种情况下,您需要将状态保留在父组件中,永远不要使用$children$root。如果您使用其中任何一个,那么现在您的组件与包含组件紧密耦合,这会破坏vue.js作为框架的理念。 - Archeg
@Archeg 所以我们应该检查父类是否有该方法,如果有,则调用它。如果您有更好的想法,请发表您的意见。 - Zoha
@Zoha 抱歉,我仍然认为这不是一个好主意,我真的建议你不要编写使用$children$root的代码,除非真的必要。请查看我的答案。 - Archeg

1

这是一种更好的方法,不使用不安全的$root$children

<template>
  <div class="hello">
    <button class="btn" @click="activeButton = 0" v-bind:style="{'background-color':buttonColor[0]}">Click me!</button>
    <button class="btn" @click="activeButton = 1" v-bind:style="{'background-color':buttonColor[1]}">Click me!</button>
    <button class="btn" @click="activeButton = 2" v-bind:style="{'background-color':buttonColor[2]}">Click me!</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      activeButton: 0
    };
  },
  computed: { 
    buttonColor: function() {
      let result = [];
      for (var i = 0; i< 3; i++){
        if (this.activeButton == i){
          result.push('black');
        } else {
          result.push('green');
        }
      }

      return result;
    }
  }
};
</script>

示例: https://codesandbox.io/s/8kz9y0rjj9

您还可以将button包装在单独的组件中,如@Zoha所建议的那样,但鉴于您没有要求,我没有这样做。这将允许在组件中隐藏buttonColor的实现。

此外,请注意,使用类是更可取和更清洁的方法:无需使用丑陋的buttonColor计算函数。

<template>
  <div class="hello">
    <button class="btn" @click="activeButton = 0" v-bind:class="{'greenBtn':true, 'blackBtn': activeButton == 0}">Click me!</button>
    <button class="btn" @click="activeButton = 1" v-bind:class="{'greenBtn':true, 'blackBtn': activeButton == 1}">Click me!</button>
    <button class="btn" @click="activeButton = 2" v-bind:class="{'greenBtn':true, 'blackBtn': activeButton == 2}">Click me!</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      activeButton: 0
    };
  },
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
 .greenBtn {
   background-color: green
 }

 .blackBtn {
   background-color: black
 }
</style>

感谢分享,@Archeg。这是个好主意。但是你能否解释一下为什么我们不应该在Vue中使用$root或$children?我在几个项目中都用到了它们,但目前还没有出现问题 :)。 - Zoha
1
@Zoha 的想法是每个组件都不应该知道它运行的环境(也不应该需要任何东西),除了显式提供给组件的属性。你的组件隐含要求其父级具有 inactiveAllButtons() 方法。组件是构建块,应该在任何地方使用而不需要上下文。如果您的构建块需要一些隐含规则,它们就不再是组件,并且变得更难处理。相反,您应该使用 props$emit() - Archeg
@Zoha 很抱歉我找不到一篇好的文章让你阅读(我很难找到好的文章),但我相信如果你在SO上问一个问题,为什么组件不应该知道父级,有人会写出更好的解释。只需查看$parent属性文档:https://vuejs.org/v2/api/#parent 请谨慎使用$parent和$children - 它们主要用作逃生口。优先使用props和events进行父子通信。 - Archeg
@Zoha 我想指出的是,这并不意味着你应该立即更改你的代码。如果它能正常工作 - 那就很好了。我只是在尝试遵循最佳实践来回答 SO 上的问题,因为人们在这里学习,一旦他们习惯了不良实践,就极难改变他们的思维方式。 - Archeg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接