做搜索框的历史记录时遇到的问题?

1.做搜索框的历史记录的时候 历史记录是搜索框聚焦时且没有内容时展示
然后点击历史记录的某一条会把这个记录给input然后触发搜索 肯定也会关闭历史记录
但是点击历史记录的时候 触发了input的失焦 然后历史记录点击事件不生效了
尝试过点历史记录的时候 给300延迟关闭历史记录 感觉体验不好
也试过历史记录给透明度0 但是关闭了也能点着肯定是不用说的 所以问下大佬们有什么解决方法吗
2.还有个问题是 search组件点清除图标的时候 它是算清空输入框并且失焦的
触发了onChange 然后满足了搜索框没有内容展示 但是它会自动失焦 (失焦在前 onChange在后) 最后用了个开关 点击了清除图标 开关设置true onChange的时候开关为true仍然不给展示
感觉是很笨的方法(可能是本身实现思路就不大好) 所以虽然有效但是期待更好的方法和思路

阅读 2.2k
avatarAI BotBETA
  1. 搜索框历史记录问题

解决点击历史记录导致input失焦的问题,可以通过阻止事件冒泡来实现。当点击历史记录时,先阻止点击事件的冒泡,然后再执行聚焦input的操作,这样可以确保点击历史记录不会触发input的失焦事件。

// 假设historyList是历史记录的数组
// handleClick是处理历史记录点击的方法
historyList.forEach((item, index) => {
  const div = document.createElement('div');
  div.textContent = item;
  div.addEventListener('click', (e) => {
    e.stopPropagation(); // 阻止事件冒泡
    handleClick(item); // 处理点击事件
  });
});
  1. 清除图标问题

对于清除图标导致搜索框自动失焦的问题,可以通过在清除图标上添加一个标志位,用来判断是否是用户点击了清除图标。当用户点击清除图标时,先修改标志位,然后在失焦事件中检查该标志位,如果标志位为true,则不触发失焦操作。

// 假设clearIcon是清除图标的DOM节点
// isClearIconClicked是标志位,初始为false
clearIcon.addEventListener('click', () => {
  isClearIconClicked = true; // 修改标志位为true
  input.value = ''; // 清空输入框
});

input.addEventListener('blur', () => {
  if (isClearIconClicked) { // 检查是否是用户点击了清除图标
    isClearIconClicked = false; // 修改标志位为false
  } else {
    // 如果不是用户点击了清除图标,正常处理失焦事件
  }
});

这样就可以解决清除图标导致搜索框自动失焦的问题。同时,通过在历史记录上添加事件监听器并阻止事件冒泡,也可以解决点击历史记录导致input失焦的问题。

2 个回答

更新:
问题一:
加一个容器元素把inputul包起来,ref要加到容器上。

问题二:
clean元素的点击事件中控制ul的显示。


我的做法是,不要依赖blur事件。

代码很简单,有不明白的可以问我。

import React, { useRef, useState, useEffect } from 'react';

import './style.css';

const options = [
  {
    id: 1,
    name: '1',
  },
  {
    id: 2,
    name: '2',
  },
  {
    id: 3,
    name: '3',
  },
];

function App() {
  const containerRef = useRef(null);
  const [visible, setVisible] = useState(false);

  // 点击历史记录区域外,关闭历史记录
  useEffect(() => {
    const onClickOutside = (event) => {
      if (
        containerRef.current &&
        !containerRef.current.contains(event.target)
      ) {
        setVisible(false);
      }
    };
    document.addEventListener('mousedown', onClickOutside);
    return () => {
      document.removeEventListener('mousedown', onClickOutside);
    };
  }, [containerRef]);

  const onFocus = () => {
    console.log('onFocus');
    setVisible(true);
  };

  const onClick = () => {
    // 选项被点击后,手动关闭历史记录 掌控权在你手中
    setVisible(false);
  };

  const onClean = () => {
    setVisible(false);
  };

  return (
    <div>
      <h1>stackblitz</h1>
      <div ref={containerRef}>
        <input type="text" onFocus={onFocus} />
        <button onClick={onClean}>clean</button>
        <ul style={{ display: visible ? 'block' : 'none' }}>
          {options.map((item) => (
            <li className="item" onClick={onClick} key={item.id}>
              {item.name}
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

export default App;
// style.css
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 1rem;
  font-family: system-ui, sans-serif;
  color: black;
  background-color: white;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.item {
  cursor: pointer;
  background: #ccc;
  border-bottom: 1px solid #eee;
}

vue的版本

<script setup>
import { ref } from 'vue';

const visible = ref(false);

const options = [
  {
    id: 1,
    name: '1',
  },
  {
    id: 2,
    name: '2',
  },
  {
    id: 3,
    name: '3',
  },
];

const hideSelct = () => {
  visible.value = false;
};

const onFocus = () => {
  visible.value = true;
};

const onClick = () => {
  hideSelct();
};

const onClean = () => {
  hideSelct();
};

const vClickOutside = {
  mounted(el, binding) {
    const clickOutsideEvent = (event) => {
      if (!el.contains(event.target)) {
        binding.value();
      }
    };

    document.addEventListener('mousedown', clickOutsideEvent);
  },
  unmounted(el) {
    document.removeEventListener('mousedown', clickOutsideEvent);
  },
};
</script>

<template>
  <div v-click-outside="hideSelct">
    <input type="text" @focus="onFocus" />
    <button @click="onClean">clean</button>
    <ul v-show="visible">
      <li v-for="option in options" :key="option.id" @click="onClick">
        {{ option.name }}
      </li>
    </ul>
  </div>
</template>

<style>
* {
  padding: 0;
  margin: 0;
}

body {
  padding: 20px;
}

ul {
  list-style: none;
  background: #ccc;
}

li {
  cursor: pointer;
  padding: 5px;
}
</style>

是这个意思?->DEMO

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题