可以使用e.target.dataset.xxx获取data-xxx的值

本文介绍了一种使用JavaScript为表格元素绑定点击事件的方法。通过此方法,当点击表格中的'查看'、'编辑'或'删除'链接时,可以触发相应功能。具体实现了如何通过事件委托的方式为动态生成的内容添加事件监听。

使用

    <td class="col-sm-2">
        <a href="#" class="check" data-awardid="${award.id}">查看</a>
        <a href="#" class="edit" data-awardid="${award.id}">编辑</a>
        <a href="#" class="delete" data-awardid="${award.id}">删除</a>
    </td>
    $("table").on('click',function(e){
      if(e.target.classList.contains("check")){
        var awardid = e.target.dataset.awardid
        awardDetail(awardid)
      }
    })

<template> <view class="dropdown-wrapper"> <!-- 插槽:触发器 --> <slot name="reference" :open="open" :close="close" :toggle="toggle"> <!-- 默认触发器 --> <view class="dropdown-trigger-default" data-dropdown-trigger @tap="open" > 点击选择 ▼ </view> </slot> <!-- 浮动下拉菜单 --> <view v-if="visible" ref="dropdownRef" class="zy-popup-dropdown-menu" :style="{ top: `${top}px`, right: `${right}px`, transform: positionStyle || position }" @touchmove.stop > <!-- 使用 scroll-view 实现可靠滚动 --> <scroll-view class="dropdown-content" :style="{ maxHeight: props.maxHeight + 'px' }" scroll-y @touchstart="onContentTouchStart" @touchmove="onContentTouchMove" > <view v-for="item in props.data" :key="item[props.valueKey]" class="dropdown-item" :class="{ 'is-selected': props.modelValue === item[props.valueKey] }" @tap="handleSelect(item)" > <text class="item-label">{{ item[props.labelKey] }}</text> <uni-icons v-show="props.modelValue === item[props.valueKey]" class="icon-check" :type="checkIcon" color="#0f56d5" /> </view> </scroll-view> </view> </view> </template> <script setup> import { ref, nextTick, onUnmounted } from 'vue' // ---------------------- // Props 定义 // ---------------------- const props = defineProps({ // 数据源 [{ label: 'xxx', value: '1' }] data: { type: Array, required: true }, // 当前选中(v-model) modelValue: { type: [String, Number, Boolean, null], default: null }, // 显示字段名 labelKey: { type: String, default: 'name' }, // 字段名 valueKey: { type: String, default: 'value' }, // 触发器元素的 ID(保留兼容性,但不再强制使用) trigger: { type: String, default: '', required: true }, // 选中图标 checkIcon: { type: String, default: 'checkmarkempty' }, // 页面滚动是否关闭下拉框 scrollClose: { type: Boolean, default: true }, // 最大高度(px) maxHeight: { type: Number, default: 486 }, // 自定义定位 transform positionStyle: { type: String, default: '' } }) // ---------------------- // Emits // ---------------------- const emit = defineEmits(['update:modelValue', 'change', 'open', 'close']) // ---------------------- // 内部状态 // ---------------------- const visible = ref(false) const top = ref(0) const left = ref(0) const right = ref(12) const position = ref('translateY(8px)') const dropdownRef = ref(null) let observer = null let startY, startScrollTop // ---------------------- // 触摸事件处理(用于防止滚动穿透) // ---------------------- const onContentTouchStart = e => { const el = e.currentTarget startY = e.touches?.[0]?.clientY || 0 startScrollTop = el?.scrollTop || 0 } const onContentTouchMove = e => { const el = e.currentTarget if (!el) return const endY = e.touches?.[0]?.clientY if (!endY) return const direction = startY - endY > 0 ? 'down' : 'up' // 到达顶部且向上滑动 → 允许页面滚动 if (startScrollTop <= 0 && direction === 'up') return // 到达底部且向下滑动 → 允许外层滚动 if (el.scrollHeight - el.scrollTop <= el.clientHeight + 1 && direction === 'down') return // 否则阻止默认行为 e.preventDefault() } // ---------------------- // 【关键修改】不再依赖 id 查询,改为从事件获取 rect // ---------------------- let lastTriggerRect = null // 缓存最后一次触发器位置 const open = async (e) => { if (visible.value) { close() return } // ✅ 使用事件对象获取触发器位置 if (e && e.target) { const view = uni.createSelectorQuery() view .select(`#${e.target.id || ''}, [data-dropdown-trigger]`) .boundingClientRect(rect => { if (rect) { lastTriggerRect = rect doOpen(rect) } else { console.warn('[Dropdown] 无法获取触发器位置,请检查结构') } }) .exec() } else { // 回退:尝试查 props.trigger(兼容旧写法) uni .createSelectorQuery() .select('#' + props.trigger) .boundingClientRect(rect => { if (rect) { lastTriggerRect = rect doOpen(rect) } }) .exec() } } // ---------------------- // 打开逻辑保持不变 // ---------------------- const doOpen = rect => { const res = uni.getSystemInfoSync() const windowHeight = Number(res.windowHeight) const windowWidth = Number(res.windowWidth) const itemHeight = 46 const menuHeight = Math.min(props.maxHeight, (props.data.length + 1) * itemHeight) const menuWidth = 215 const gap = 8 const spaceBelow = windowHeight - rect.bottom const spaceAbove = rect.top const isBothInsufficient = spaceAbove < menuHeight && spaceBelow < menuHeight let finalTop, finalLeft, finalTransform if (isBothInsufficient) { finalTop = windowHeight / 2 finalTransform = 'translate(0, -50%)' finalLeft = windowWidth - menuWidth - 12 if (finalLeft < 0) finalLeft = 0 } else { const needUpward = spaceBelow < menuHeight finalTop = needUpward ? rect.top - menuHeight - gap : rect.bottom + gap finalTransform = needUpward ? 'translateY(8px)' : 'translateY(-8px)' finalLeft = windowWidth - menuWidth - 12 if (finalLeft < 0) finalLeft = 0 } top.value = finalTop left.value = finalLeft position.value = finalTransform visible.value = true emit('open', props.modelValue, props.trigger) nextTick(() => { bindOutsideClickListener() bindScrollListener() }) } // ---------------------- // 关闭 & 切换 & 选择 // ---------------------- const close = () => { if (!visible.value) return visible.value = false emit('close', props.trigger) removeListeners() } const toggle = () => { visible.value ? close() : open() } const handleSelect = item => { const value = item[props.valueKey] const label = item[props.labelKey] const selected = { [props.valueKey]: value, [props.labelKey]: label } if (props.modelValue !== value) { emit('update:modelValue', value, props.trigger) emit('change', selected, props.trigger) } close() } // ---------------------- // 外部点击关闭(增强判断) // ---------------------- const bindOutsideClickListener = () => { const handler = e => { const path = e.path || [] const isInside = path.some(node => node === dropdownRef.value?.$el || node?.classList?.contains('zy-popup-dropdown-menu') || node?.dataset?.dropdownTrigger ) if (!isInside) close() } uni.$on('onPageTap', handler) observer = { ...observer, cleanupTap: () => uni.$off('onPageTap', handler) } } // ---------------------- // 页面滚动关闭 // ---------------------- const bindScrollListener = () => { const scrollHandler = () => { if (!visible.value) return if (props.scrollClose) { close() } else if (lastTriggerRect) { reposition(lastTriggerRect) } } uni.$on('onPageScroll', scrollHandler) observer = { ...observer, cleanupScroll: () => uni.$off('onPageScroll', scrollHandler) } } // ---------------------- // 重新定位(简化) // ---------------------- const reposition = () => { if (lastTriggerRect) { doOpen(lastTriggerRect) } } // ---------------------- // 移除监听 // ---------------------- const removeListeners = () => { observer?.cleanupTap?.() observer?.cleanupScroll?.() observer = null } // ---------------------- // 暴露方法 // ---------------------- defineExpose({ open, close, toggle }) // ---------------------- // 卸载清理 // ---------------------- onUnmounted(() => { removeListeners() }) </script> <style scoped> /* 整体容器 */ .dropdown-wrapper { display: inline-block; } /* 默认触发器样式 */ .dropdown-trigger-default { display: inline-flex; align-items: center; justify-content: center; padding: 8px 16px; background-color: #fff; border: 1px solid #ddd; border-radius: 6px; font-size: 15px; color: #333; cursor: pointer; } /* 下拉菜单主体 */ .zy-popup-dropdown-menu { position: fixed; width: 215px; background-color: #ffffff; border-radius: 8px; z-index: 9999; display: flex; flex-direction: column; box-shadow: 0 4px 5px -3px rgba(0, 0, 0, 0.08), 0 8px 12px 1px rgba(0, 0, 0, 0.04), 0 3px 15px 3px rgba(0, 0, 0, 0.05); } /* 内容区可滚动 */ .dropdown-content { max-height: 486px; } /* 每一项 */ .dropdown-item { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; font-size: 15px; color: #333333; border-bottom: 1px solid #f5f5f5; } .dropdown-item:last-child { border-bottom: none; } .dropdown-item.is-selected { color: #0f56d5; font-weight: 500; } .item-label { flex: 1; word-break: break-word; line-height: 1.4; text-align: left; } .icon-check { font-family: 'erda' !important; font-size: 16px; margin-left: 8px; color: #0f56d5; } </style> 当前组件被组件套用后依旧无法正常展示
最新发布
11-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值