You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制特定标识元素拖放到指定区域?Vue.js与原生JS实现

拖拽元素与放置区域的匹配限制实现方案(原生JS + Vue.js)

需求明确

阻止带有特定标识的元素被拖放到受限放置区域,仅允许标识为type-one的元素放入drop-zone-one,标识为type-two的元素放入drop-zone-two;拖拽不允许的元素时,放置区域需锁定并显示视觉提示(比如红色边框/背景)。禁止使用Sortable、Draggable等第三方拖拽库。


一、原生JavaScript实现

修正后的HTML(原示例重复id不符合规范,改为class标识类型)

<div draggable="true" class="type-one">Item 1, Type 1</div>  
<div draggable="true" class="type-one">Item 2, Type 1</div>  

<div draggable="true" class="type-two">Item 1, Type 2</div>
<div draggable="true" class="type-two">Item 2, Type 2</div>

<div id="drop-zone-one" class="drop-zone">
  仅允许Type 1元素放入
</div>

<div id="drop-zone-two" class="drop-zone">
  仅允许Type 2元素放入
</div>

CSS视觉提示样式

.drop-zone {
  width: 300px;
  height: 200px;
  border: 2px dashed #ccc;
  margin: 10px 0;
  padding: 20px;
  transition: all 0.3s;
}
.drop-zone.allowed {
  border-color: #2ecc71;
  background-color: rgba(46, 204, 113, 0.1);
}
.drop-zone.forbidden {
  border-color: #e74c3c;
  background-color: rgba(231, 76, 60, 0.1);
  cursor: not-allowed;
}

JavaScript逻辑实现

// 存储当前拖拽元素的类型
let currentDragType = '';

// 绑定拖拽开始事件
document.querySelectorAll('[draggable="true"]').forEach(item => {
  item.addEventListener('dragstart', (e) => {
    currentDragType = item.classList.contains('type-one') ? 'type-one' : 'type-two';
    e.dataTransfer.setData('text/plain', item.textContent);
  });
});

// 处理放置区域的拖拽事件
document.querySelectorAll('.drop-zone').forEach(zone => {
  // 拖拽悬停:必须阻止默认行为才能触发drop
  zone.addEventListener('dragover', (e) => {
    e.preventDefault();
    const isAllowed = (zone.id === 'drop-zone-one' && currentDragType === 'type-one') || 
                      (zone.id === 'drop-zone-two' && currentDragType === 'type-two');
    
    zone.classList.remove('allowed', 'forbidden');
    zone.classList.add(isAllowed ? 'allowed' : 'forbidden');
  });

  // 拖拽离开区域时重置样式
  zone.addEventListener('dragleave', () => {
    zone.classList.remove('allowed', 'forbidden');
  });

  // 放置元素事件
  zone.addEventListener('drop', (e) => {
    e.preventDefault();
    const isAllowed = (zone.id === 'drop-zone-one' && currentDragType === 'type-one') || 
                      (zone.id === 'drop-zone-two' && currentDragType === 'type-two');
    
    if (isAllowed) {
      const newItem = document.createElement('div');
      newItem.textContent = e.dataTransfer.getData('text/plain');
      newItem.classList.add(currentDragType);
      zone.appendChild(newItem);
    }
    zone.classList.remove('allowed', 'forbidden');
  });
});

二、Vue.js实现

完整Vue组件代码

<template>
  <div class="drag-container">
    <!-- 可拖拽元素 -->
    <div 
      v-for="(item, index) in itemsListOne" 
      :key="index"
      draggable="true"
      @dragstart="handleDragStart('type-one')"
      class="drag-item type-one"
    >
      {{ item.name }}
    </div>

    <div 
      v-for="(item, index) in itemsListTwo" 
      :key="index"
      draggable="true"
      @dragstart="handleDragStart('type-two')"
      class="drag-item type-two"
    >
      {{ item.name }}
    </div>

    <!-- 放置区域1 -->
    <div 
      id="drop-zone-one"
      class="drop-zone"
      @dragover.prevent="handleDragOver($event, 'zone-one')"
      @drop="handleDrop($event, 'zone-one')"
      @dragleave="handleDragLeave('zone-one')"
      :class="{ allowed: zoneOneStatus === 'allowed', forbidden: zoneOneStatus === 'forbidden' }"
    >
      仅允许Type 1元素放入
      <div v-for="(item, index) in droppedZoneOne" :key="index" class="drag-item">{{ item }}</div>
    </div>

    <!-- 放置区域2 -->
    <div 
      id="drop-zone-two"
      class="drop-zone"
      @dragover.prevent="handleDragOver($event, 'zone-two')"
      @drop="handleDrop($event, 'zone-two')"
      @dragleave="handleDragLeave('zone-two')"
      :class="{ allowed: zoneTwoStatus === 'allowed', forbidden: zoneTwoStatus === 'forbidden' }"
    >
      仅允许Type 2元素放入
      <div v-for="(item, index) in droppedZoneTwo" :key="index" class="drag-item">{{ item }}</div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      itemsListOne: [{ name: 'Item 1, Type 1' }, { name: 'Item 2, Type 1' }],
      itemsListTwo: [{ name: 'Item 1, Type 2' }, { name: 'Item 2, Type 2' }],
      droppedZoneOne: [],
      droppedZoneTwo: [],
      currentDragType: '',
      zoneOneStatus: '',
      zoneTwoStatus: ''
    };
  },
  methods: {
    handleDragStart(type) {
      this.currentDragType = type;
    },
    handleDragOver(e, zone) {
      const isAllowed = (zone === 'zone-one' && this.currentDragType === 'type-one') || 
                        (zone === 'zone-two' && this.currentDragType === 'type-two');
      
      this[zone === 'zone-one' ? 'zoneOneStatus' : 'zoneTwoStatus'] = isAllowed ? 'allowed' : 'forbidden';
    },
    handleDragLeave(zone) {
      this[zone === 'zone-one' ? 'zoneOneStatus' : 'zoneTwoStatus'] = '';
    },
    handleDrop(e, zone) {
      const itemText = e.dataTransfer.getData('text/plain');
      const isAllowed = (zone === 'zone-one' && this.currentDragType === 'type-one') || 
                        (zone === 'zone-two' && this.currentDragType === 'type-two');
      
      if (isAllowed) {
        this[zone === 'zone-one' ? 'droppedZoneOne' : 'droppedZoneTwo'].push(itemText);
      }
      
      // 重置所有区域状态
      this.zoneOneStatus = '';
      this.zoneTwoStatus = '';
    }
  }
};
</script>

<style scoped>
.drag-item {
  padding: 8px 12px;
  margin: 5px 0;
  background: #f5f5f5;
  border-radius: 4px;
  cursor: grab;
}
.drop-zone {
  width: 300px;
  height: 200px;
  border: 2px dashed #ccc;
  margin: 15px 0;
  padding: 20px;
  transition: all 0.3s;
}
.drop-zone.allowed {
  border-color: #2ecc71;
  background-color: rgba(46, 204, 113, 0.1);
}
.drop-zone.forbidden {
  border-color: #e74c3c;
  background-color: rgba(231, 76, 60, 0.1);
  cursor: not-allowed;
}
</style>

关键说明

  • 用currentDragType存储当前拖拽元素的类型,通过@dragstart事件赋值
  • 放置区域通过@dragover.prevent阻止默认行为(否则无法触发drop事件),同时判断是否允许放置并更新样式状态
  • drop事件中仅在允许的情况下将元素添加到对应放置区域的列表中
  • 拖拽离开时重置区域状态,移除视觉提示

内容的提问来源于stack exchange,提问作者display_name

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 21:35:22