Vue.js:如何在同一元素上处理单击和双击事件

48

我有一个Vue组件,其中单击和双击有不同的事件。单击可(取消)选择行,双击可打开编辑表单。

<ul class="data_row"
  v-for="(row,index) in gridData"
  @dblclick="showEditForm(row,$event)"
  @click="rowSelect(row,$event)"
>

这样做,我会在双击时触发3个事件。两个点击事件和最后一个双击事件。由于点击事件首先触发,除了延迟一定的毫秒数来延迟点击事件外,是否有其他方法可以停止双击时点击事件的传播?

JSFiddle示例在这里


@dblclick.stop="showEditForm(row,$event)" 的翻译是什么? - Belmin Bedak
@Belmin Bedak 很抱歉,行为没有任何改变。 - Corwin
1
请参考 https://dev59.com/3W035IYBdhLWcg3wW-pa,主要思路相同。 - sobolevn
9个回答

41

如评论中建议的那样,您可以通过设置一段特定时间(比如 x 毫秒)的计时器来模拟双击事件。

  • 如果在这段时间内没有再次点击,就执行单击函数。
  • 如果发生第二次点击,则调用双击函数。
  • 计时器会在收到第二个点击时被清除。
  • 当 x 毫秒经过后,计时器也会被清除。

请参见以下代码和工作示例fiddle

new Vue({
    el: '#app',
    data: {
        result: [],
        delay: 700,
        clicks: 0,
        timer: null
    },    
     mounted: function() {
        console.log('mounted');
     },      
     methods: {
        oneClick(event) {
          this.clicks++;
          if (this.clicks === 1) {
            this.timer = setTimeout( () => {
              this.result.push(event.type);
              this.clicks = 0
            }, this.delay);
          } else {
             clearTimeout(this.timer);  
             this.result.push('dblclick');
             this.clicks = 0;
          }         
        }      
     }
});

1
这是一个不错的解决方案,但在某些情况下,在延迟结束后处理单击可能有点不直观。把第一次点击视为立即单击(即在调用setTimeout之前移动self.result.push(event.type);)是否更好?然后,如果在延迟期间发生第二次点击,则为双击。文件浏览器实际上就是这样工作的:单击文件会立即选择,但如果快速进行第二次点击,则会打开该文件。 - Sbu
@Sbu 这取决于功能应该是什么。 OP 询问如何触发第一个或第二个函数,而不是第一个或两个函数(在不久之后触发 dblclick 事件的情况下询问如何停止 click 事件的传播),这意味着提供的答案(只触发其中一个函数)在给定上下文中是最正确的。 - Vaclav Pelc

14
<div id="example-1">
 <button v-on:dblclick="counter += 1, funcao()">Add 1</button>
   <p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
 el: '#example-1',
 data: {
   counter: 0
 },
 methods: {
   funcao: function(){
     alert("Sou uma funcao");
   }
 }
})

看看这个可用的代码片段:https://codepen.io/robertourias/pen/LxVNZX


13
问题不在于 @dblclick 无法工作,而是您不能同时使用 @click@dblclick - Aaron Butacov

12

我认为我有一个更简单的解决方案(我正在使用vue-class,但相同的原则适用):

private timeoutId = null;
onClick() {
        if(!this.timeoutId)
        {
            this.timeoutId = setTimeout(() => {
                // simple click
            }, 50);//tolerance in ms
        }else{
            clearTimeout(this.timeoutId);
            // double click
        }
    }

它不需要计算点击次数。


6
谢谢!我还在清除超时后添加了this.timeoutId = null; - Jekis

5

点击之间的时间必须很短。

为了获取单击和双击,只需要一个计数器来记录点击数量(例如0.2秒),并足以在用户点击缓慢或执行多个操作时捕捉到其意图;这就是双击或默认情况。

我在此处提供实现这些功能的代码。

new Vue({
   el: '#app',
   data: {numClicks:0, msg:''},
   methods: {
      // detect click event
      detectClick: function() {
        this.numClicks++;
        if (this.numClicks === 1) {          // the first click in .2s
            var self = this;
            setTimeout(function() {
                switch(self.numClicks) {     // check the event type
                      case 1:
                        self.msg = 'One click';
                        break;
                      default:
                        self.msg = 'Double click';
                }
                self.numClicks = 0;               // reset the first click
            }, 200);                              // wait 0.2s
        } // if
      }  // detectClick function
   }
});
span { color: red }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.0/vue.js"></script>

<div id='app'>
  <button @click='detectClick'>
    Test Click Event, num clicks
    <span>{{ numClicks }}</span>
   </button>
  <h2>Last Event: <span>{{ msg }}</span></h2>
</div>


5

我用这种方法解决同样的问题。我使用一个 promise,它要么在触发 200 毫秒的超时后被解析,要么在检测到第二次点击时被解析。在我的最近的 web 应用程序中,它运行得非常好。

<div id="app">
  <div 
    @click="clicked().then((text) => {clickType = text})">
      {{clickType}}
  </div>
</div>

<script>
new Vue({
  el: "#app",
  data: {
    click: undefined,
    clickType: 'Click or Doubleclick ME'
  },
  methods: {
    clicked () {
      return new Promise ((resolve, reject) => {
        if (this.click) {
          clearTimeout(this.click)
          resolve('Detected DoubleClick')
        }
        this.click = setTimeout(() => {
         this.click = undefined
         resolve('Detected SingleClick')
        }, 200)
      })
    }
  }
})
</script>

可用的示例代码:

https://jsfiddle.net/MapletoneMartin/9m62Lrwf/

2
实际上,您可以删除@dblclick事件处理程序,因为所有单击事件都仅由@click处理程序处理! - Alexander Remesch
谢谢@AlexanderRemesch!你当然是正确的。我编辑了我的帖子。 - MartinSRimsbo

4

Vue组件

// html 
 <div class="grid-content">
    <el-button 
   @click.native="singleClick" 
   @dblclick.native="doubleClick" 
   class="inline-cell">
    click&dbclickOnSameElement</el-button>
 </div>

// script
<script>
let time = null;  // define time be null 
export default {
  name: 'testComponent',
  data() {
    return {
       test:''
    };
  },
  methods: {

   singleClick() {
     // first clear  time
      clearTimeout(time);
      time = setTimeout(() => {
        console.log('single click ing')
      }, 300); 
    },
  
   doubleClick() {
      clearTimeout(time);  
      console.log('double click ing');
    }
  }
}
</script>

0

@click.stop 处理单击事件,@dblclick.stop 处理双击事件

<v-btn :ripple="false"
            class="ma-0"
            @click.stop="$emit('editCompleteGrvEvent', props.item)"
            @dblclick.stop="$emit('sendCompleteGrvEvent',props.item)">
    <v-icon>send</v-icon>
    </v-btn>

0

除非你需要对单个选择进行昂贵的操作,否则你可以将rowSelect重构为toggle。设置一个简单的数组会比设置和取消定时器更快、更可靠、更直接。如果点击事件触发两次,这并不重要,但你可以在edit函数中轻松处理它。

<template>
  <ul>
    <li :key="index" v-for="(item, index) in items">
      <a
        :class="{ 'active-class': selected.indexOf(item) !== -1 }"
        @click="toggleSelect(item)"
        @dblclick="editItem(item)"
      >
        {{ item.title }}
      </a>
      <!-- Or use a checkbox with v-model
      <label @dblclick="editItem(item)">
        <input type="checkbox" :value="item.id" v-model.lazy="selected" />
        {{ item.title }}
      </label>
      -->
    </li>
  </ul>
</template>

<script>
export default {
  data: function () {
    return {
      items: [
        {
          id: 1,
          title: "Item 1",
        },
        {
          id: 2,
          title: "Item 2",
        },
        {
          id: 3,
          title: "Item 3",
        },
      ],
      selected: [],
    };
  },

  methods: {
    editItem(item) {
      /*
       * Optionally put the item in selected
       * A few examples, pick one that works for you:
       */

      // this.toggleSelect(item); // If the item was selected before dblclick, it will still be selected. If it was unselected, it will still be unselected.

      // this.selected = []; // Unselect everything.

      // Make sure this item is selected:
      // let index = this.selected.indexOf(item.id);
      // if (index === -1) {
      //   this.selected.push(item.id);
      // }

      // Make sure this item is unselected:
      // let index = this.selected.indexOf(item.id);
      // if (index !== -1) {
      //   this.selected.splice(index, 1);
      // }

      this.doTheThingThatOpensTheEditorHere(item);
    },

    toggleSelect(item) {
      let index = this.selected.indexOf(item.id);
      index === -1
        ? this.selected.push(item.id)
        : this.selected.splice(index, 1);
    },

    // For fun, get an array of items that are selected:
    getSelected() {
      return this.items.filter((item) => this.selected.indexOf(item.id) !== -1);
    },
  },
};
</script>

0
selectedFolder = ''; // string of currently selected item
folderSelected = false; // preview selected item

selectFolder(folder) {
    if (this.selectedFolder == folder) {
        // double click
      this.folderSelected = false;
      this.$store.dispatch('get_data_for_this_folder', folder);
    } else {
      // single click
      this.selectedFolder = folder;
      this.folderSelected = true;
    }
},

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