事件处理 #

一、基本事件绑定 #

1.1 内联事件 #

svelte
<script>
  let count = 0;
</script>

<button onclick={() => count += 1}>
  Clicked {count} times
</button>

1.2 方法引用 #

svelte
<script>
  let count = 0;
  
  function increment() {
    count += 1;
  }
  
  function add(amount) {
    count += amount;
  }
</script>

<button onclick={increment}>
  Increment
</button>

<button onclick={() => add(5)}>
  Add 5
</button>

1.3 事件对象 #

svelte
<script>
  function handleClick(event) {
    console.log('Event type:', event.type);
    console.log('Target:', event.target);
    console.log('Current target:', event.currentTarget);
  }
</script>

<button onclick={handleClick}>
  Click me
</button>

1.4 传递参数和事件对象 #

svelte
<script>
  function handleClick(item, event) {
    console.log('Item:', item);
    console.log('Event:', event);
  }
</script>

<button onclick={(e) => handleClick('button1', e)}>
  Click with data
</button>

二、事件修饰符 #

2.1 preventDefault #

svelte
<script>
  function handleSubmit() {
    console.log('Form submitted');
  }
</script>

<form onsubmit|preventDefault={handleSubmit}>
  <input type="text" />
  <button type="submit">Submit</button>
</form>

2.2 stopPropagation #

svelte
<script>
  function handleDivClick() {
    console.log('Div clicked');
  }
  
  function handleButtonClick() {
    console.log('Button clicked');
  }
</script>

<div onclick={handleDivClick}>
  <button onclick|stopPropagation={handleButtonClick}>
    Button
  </button>
</div>

2.3 修饰符列表 #

修饰符 说明
preventDefault 阻止默认行为
stopPropagation 阻止事件冒泡
stopImmediatePropagation 阻止同一元素上的其他监听器
capture 使用捕获模式
once 只触发一次
self 只在 event.target 是当前元素时触发
passive 提升滚动性能
nonpassive 禁用 passive
trusted 只响应可信事件

2.4 组合修饰符 #

svelte
<button onclick|preventDefault|stopPropagation|once={handleClick}>
  Multiple Modifiers
</button>

2.5 self 修饰符 #

svelte
<script>
  function handleClick() {
    console.log('Clicked on div itself');
  }
</script>

<div onclick|self={handleClick} class="container">
  <p>Clicking this paragraph won't trigger the handler</p>
  <button>Clicking this button won't trigger the handler</button>
</div>

2.6 once 修饰符 #

svelte
<script>
  let clicked = false;
  
  function handleClick() {
    clicked = true;
    console.log('This will only log once');
  }
</script>

<button onclick|once={handleClick}>
  {clicked ? 'Already clicked' : 'Click once'}
</button>

三、键盘事件 #

3.1 基本键盘事件 #

svelte
<script>
  let lastKey = '';
  
  function handleKeyDown(event) {
    lastKey = event.key;
    console.log('Key:', event.key);
    console.log('Code:', event.code);
    console.log('Ctrl:', event.ctrlKey);
    console.log('Shift:', event.shiftKey);
    console.log('Alt:', event.altKey);
    console.log('Meta:', event.metaKey);
  }
</script>

<input 
  onkeydown={handleKeyDown}
  placeholder="Press any key"
/>
<p>Last key: {lastKey}</p>

3.2 快捷键处理 #

svelte
<script>
  function handleKeyDown(event) {
    if (event.key === 'Enter' && event.ctrlKey) {
      console.log('Ctrl + Enter pressed');
    }
    
    if (event.key === 's' && event.ctrlKey) {
      event.preventDefault();
      console.log('Save shortcut');
    }
    
    if (event.key === 'Escape') {
      console.log('Escape pressed');
    }
  }
</script>

<textarea 
  onkeydown={handleKeyDown}
  placeholder="Ctrl+Enter to submit, Ctrl+S to save"
/>

3.3 Svelte 5 语法 #

svelte
<script>
  function handleKeyDown(event) {
    if (event.key === 'Enter') {
      console.log('Enter pressed');
    }
  }
</script>

<input onkeydown={handleKeyDown} />

四、鼠标事件 #

4.1 常用鼠标事件 #

svelte
<script>
  let position = { x: 0, y: 0 };
  let isPressed = false;
  
  function handleMouseMove(event) {
    position = {
      x: event.clientX,
      y: event.clientY
    };
  }
  
  function handleMouseDown() {
    isPressed = true;
  }
  
  function handleMouseUp() {
    isPressed = false;
  }
</script>

<div 
  onmousemove={handleMouseMove}
  onmousedown={handleMouseDown}
  onmouseup={handleMouseUp}
  class:pressed={isPressed}
>
  <p>Position: {position.x}, {position.y}</p>
  <p>Status: {isPressed ? 'Pressed' : 'Released'}</p>
</div>

4.2 拖拽实现 #

svelte
<script>
  let position = { x: 100, y: 100 };
  let dragging = false;
  let offset = { x: 0, y: 0 };
  
  function handleMouseDown(event) {
    dragging = true;
    offset = {
      x: event.clientX - position.x,
      y: event.clientY - position.y
    };
  }
  
  function handleMouseMove(event) {
    if (!dragging) return;
    position = {
      x: event.clientX - offset.x,
      y: event.clientY - offset.y
    };
  }
  
  function handleMouseUp() {
    dragging = false;
  }
</script>

<svelte:window 
  onmousemove={handleMouseMove}
  onmouseup={handleMouseUp}
/>

<div 
  class="draggable"
  class:dragging
  onmousedown={handleMouseDown}
  style="left: {position.x}px; top: {position.y}px;"
>
  Drag me
</div>

<style>
  .draggable {
    position: absolute;
    width: 100px;
    height: 100px;
    background: #ff3e00;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: move;
    user-select: none;
  }
  
  .draggable.dragging {
    opacity: 0.8;
  }
</style>

五、表单事件 #

5.1 输入事件 #

svelte
<script>
  let text = '';
  let debounceText = '';
  let timer;
  
  function handleInput(event) {
    text = event.target.value;
  }
  
  function handleInputDebounced(event) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      debounceText = event.target.value;
    }, 300);
  }
</script>

<input oninput={handleInput} placeholder="Type something" />
<p>Live: {text}</p>

<input oninput={handleInputDebounced} placeholder="Debounced" />
<p>Debounced: {debounceText}</p>

5.2 表单提交 #

svelte
<script>
  let form = {
    email: '',
    password: ''
  };
  
  function handleSubmit(event) {
    event.preventDefault();
    console.log('Form data:', form);
  }
</script>

<form onsubmit={handleSubmit}>
  <input 
    type="email" 
    bind:value={form.email}
    required
  />
  <input 
    type="password" 
    bind:value={form.password}
    required
  />
  <button type="submit">Submit</button>
</form>

5.3 选择事件 #

svelte
<script>
  let selectedText = '';
  
  function handleSelect(event) {
    const start = event.target.selectionStart;
    const end = event.target.selectionEnd;
    selectedText = event.target.value.substring(start, end);
  }
</script>

<textarea onselect={handleSelect} placeholder="Select some text"></textarea>
<p>Selected: {selectedText}</p>

六、自定义事件 #

6.1 创建自定义事件 #

svelte
<script>
  import { createEventDispatcher } from 'svelte';
  
  const dispatch = createEventDispatcher();
  
  function handleClick() {
    dispatch('message', {
      text: 'Hello from child!'
    });
  }
</script>

<button onclick={handleClick}>
  Send Message
</button>

6.2 监听自定义事件 #

svelte
<script>
  import Child from './Child.svelte';
  
  function handleMessage(event) {
    console.log('Received:', event.detail.text);
  }
</script>

<Child on:message={handleMessage} />

6.3 Svelte 5 回调 Props #

svelte
<script>
  let { onclick } = $props();
  
  function handleClick() {
    onclick?.({ text: 'Hello from child!' });
  }
</script>

<button onclick={handleClick}>
  Send Message
</button>
svelte
<script>
  import Child from './Child.svelte';
  
  function handleClick(data) {
    console.log('Received:', data.text);
  }
</script>

<Child onclick={handleClick} />

七、事件委托 #

7.1 列表事件委托 #

svelte
<script>
  let items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' }
  ];
  
  function handleItemClick(event) {
    const target = event.target.closest('[data-id]');
    if (target) {
      const id = parseInt(target.dataset.id);
      console.log('Clicked item:', id);
    }
  }
  
  function handleDeleteClick(event) {
    const target = event.target.closest('[data-action="delete"]');
    if (target) {
      event.stopPropagation();
      const id = parseInt(target.dataset.id);
      console.log('Delete item:', id);
    }
  }
</script>

<ul onclick={[handleItemClick, handleDeleteClick]}>
  {#each items as item}
    <li data-id={item.id}>
      {item.name}
      <button data-action="delete" data-id={item.id}>Delete</button>
    </li>
  {/each}
</ul>

八、触摸事件 #

8.1 基本触摸事件 #

svelte
<script>
  let touchPosition = { x: 0, y: 0 };
  
  function handleTouchStart(event) {
    const touch = event.touches[0];
    touchPosition = {
      x: touch.clientX,
      y: touch.clientY
    };
  }
  
  function handleTouchMove(event) {
    event.preventDefault();
    const touch = event.touches[0];
    touchPosition = {
      x: touch.clientX,
      y: touch.clientY
    };
  }
</script>

<div 
  ontouchstart={handleTouchStart}
  ontouchmove|preventDefault={handleTouchMove}
  class="touch-area"
>
  <p>Touch position: {touchPosition.x}, {touchPosition.y}</p>
</div>

8.2 手势识别 #

svelte
<script>
  let startX = 0;
  let startY = 0;
  let direction = '';
  
  function handleTouchStart(event) {
    startX = event.touches[0].clientX;
    startY = event.touches[0].clientY;
  }
  
  function handleTouchEnd(event) {
    const endX = event.changedTouches[0].clientX;
    const endY = event.changedTouches[0].clientY;
    
    const diffX = endX - startX;
    const diffY = endY - startY;
    
    if (Math.abs(diffX) > Math.abs(diffY)) {
      direction = diffX > 0 ? 'right' : 'left';
    } else {
      direction = diffY > 0 ? 'down' : 'up';
    }
  }
</script>

<div 
  ontouchstart={handleTouchStart}
  ontouchend={handleTouchEnd}
  class="gesture-area"
>
  <p>Swipe direction: {direction}</p>
</div>

九、完整示例:拖拽排序列表 #

svelte
<script>
  let items = $state([
    { id: 1, text: 'Item 1' },
    { id: 2, text: 'Item 2' },
    { id: 3, text: 'Item 3' },
    { id: 4, text: 'Item 4' },
    { id: 5, text: 'Item 5' }
  ]);
  
  let draggedItem = null;
  let draggedIndex = null;
  let dropTargetIndex = null;
  
  function handleDragStart(event, item, index) {
    draggedItem = item;
    draggedIndex = index;
    event.dataTransfer.effectAllowed = 'move';
    event.target.classList.add('dragging');
  }
  
  function handleDragEnd(event) {
    event.target.classList.remove('dragging');
    draggedItem = null;
    draggedIndex = null;
    dropTargetIndex = null;
  }
  
  function handleDragOver(event) {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
  }
  
  function handleDragEnter(event, index) {
    event.preventDefault();
    dropTargetIndex = index;
  }
  
  function handleDrop(event, index) {
    event.preventDefault();
    
    if (draggedIndex !== null && draggedIndex !== index) {
      const newItems = [...items];
      newItems.splice(draggedIndex, 1);
      newItems.splice(index, 0, draggedItem);
      items = newItems;
    }
    
    draggedItem = null;
    draggedIndex = null;
    dropTargetIndex = null;
  }
</script>

<ul class="sortable-list">
  {#each items as item, index (item.id)}
    <li 
      draggable="true"
      ondragstart={(e) => handleDragStart(e, item, index)}
      ondragend={handleDragEnd}
      ondragover={handleDragOver}
      ondragenter={(e) => handleDragEnter(e, index)}
      ondrop={(e) => handleDrop(e, index)}
      class:drop-target={dropTargetIndex === index}
    >
      {item.text}
    </li>
  {/each}
</ul>

<style>
  .sortable-list {
    list-style: none;
    padding: 0;
  }
  
  li {
    padding: 1rem;
    margin: 0.5rem 0;
    background: #f5f5f5;
    border: 1px solid #ddd;
    border-radius: 4px;
    cursor: move;
    transition: transform 0.2s, box-shadow 0.2s;
  }
  
  li.dragging {
    opacity: 0.5;
    transform: scale(1.02);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  }
  
  li.drop-target {
    border-color: #ff3e00;
    background: #fff3f0;
  }
</style>

十、总结 #

事件类型 说明
onclick 点击事件
onkeydown 键盘按下
onkeyup 键盘释放
onmousedown 鼠标按下
onmouseup 鼠标释放
onmousemove 鼠标移动
oninput 输入事件
onsubmit 表单提交
onchange 值改变

事件处理要点:

  • 使用修饰符简化常见操作
  • 合理使用事件委托
  • 注意内存泄漏,及时清理
  • 自定义事件用于组件通信
  • 触摸事件需要考虑移动端兼容
最后更新:2026-03-28