【问题标题】:Vue: Make matching part of input bold, including special hyphensVue:使输入的匹配部分加粗,包括特殊连字符
【发布时间】:2021-11-30 08:48:32
【问题描述】:

我在 Vue 中制作了一个带有搜索/过滤系统的简单选择组件。根据用户输入,我将展示一些比利时城市建议。

工作示例:https://codesandbox.io/s/wandering-lake-lecok?file=/src/components/Select.vue(Codesandbox 中有时会出现错误消息。刷新浏览器中的构建,它应该可以工作)

我想让用户体验更进一步,并用粗体和下划线显示用户输入的匹配部分。因此我有一个有效的makeBold 函数。通过将建议字符串分成多个部分,我可以添加粗体和下划线标记并返回建议。

computed: {
    results() {
        return this.options.filter((option) =>
        option.display_name
            .replaceAll("-'", "")
            .toLowerCase()
            .includes(this.searchInput.replaceAll("-'", "").toLowerCase())
        );
    },
},
methods: {
    makeBold(str, query) {
        const n = str.toUpperCase();
        const q = query.toUpperCase();
        const x = n.indexOf(q);

        if (!q || x === -1) {
            return str;
        }

        const l = q.length;

        return (
            str.substr(0, x) + "<b><u>" + str.substr(x, l) + "</u></b>" + str.substr(x + l)
        );
    },
}

一个问题,比利时的许多城市都使用破折号和/或撇号。在建议功能中,我删除了这些字符,因此用户无需键入它们。但在 makeBold 函数中,我想让这些字符加粗并加下划线。

例如:

当输入是“sint j”、“sintj”或“Sint-j”时,我希望建议看起来像“Sint-Jans-Molenbeek”和“Sint- J在't Goor'

有人可以告诉我如何实现这一目标吗?

【问题讨论】:

    标签: javascript vue.js drop-down-menu


    【解决方案1】:

    我建议使用掩码来保存城市名称结构,并在找到城市名称中子字符串的开始和结束索引后,从掩码恢复原始字符串,在开始和结束索引处插入适当的标签一个替换函数。这样您就不必担心任何其他非单词字符或其他意外的用户输入。

    这里是 makeBold 函数:

    makeBold(str, query) {
          // mask all word characters in city name
          const city_mask = str.replace(/\w/g, "#");
          // strip city and query string from any non-word character
          let query_stripped = query.toLowerCase().replace(/\W/g, "");
          let string_stripped = str.replace(/\W/g, "");
          // find the index of querystring in city name
          let index = string_stripped.toLowerCase().indexOf(query_stripped);
    
          if (index > -1 && query_stripped.length) {
            // find the end position of substring in stripped city name
            let end_index = index + query_stripped.length - 1;
    
            // replacer function for each masked character.
            // it will add to the start and end character of substring the corresponding tags,
            // replacing all masked characters with the original one.
            function replacer(i) {
              let repl = string_stripped[i];
              if (i === index) {
                repl = "<b><u>" + repl;
              }
              if (i === end_index) {
                repl = repl + "</u></b>";
              }
              return repl;
            }
            let i = -1;
            // restore masked string
            return city_mask.replace(/#/g, (_) => {
              i++;
              return replacer(i);
            });
          }
    
          return str;
        }
    

    here 是工作沙箱。我对您计算的 results 进行了一些更改,以去除所有非单词字符。

    【讨论】:

      【解决方案2】:

      一种方法是将您的搜索字符串转换为 RegExp 对象并使用字符串的replace(regexp, replacerFunction) 重载来实现此目的。
      例如搜索字符串是"sintg"

      new RegExp(this.searchInput.split("").join("-?"), "i");
      

      把它变成/s-?i-?n-?t-?g/gi
      -?表示可选的-字符,
      "i"最后是RegExp不区分大小写的标志

      应用于你得到这个的代码和框代码

      computed: {
        results() {
          const regex = new RegExp(this.searchInput.split("").join("-?"), "i");
          return this.options.filter((option) => option.display_name.match(regex));
        },
      },
      methods: {
        makeBold(str, query) {
          const regex = new RegExp(query.split("").join("-?"), "i");
          return str.replace(regex, (match) => "<b><u>" + match + "</u></b>");
        },
      },
      

      这给出了这个结果

      但是有一个警告:如果用户在搜索框中输入 RegExp 特殊符号,则会引发错误
      为避免这种情况,初始搜索输入文本需要应用 RegExp 转义。
      如:

      new RegExp(escapeRegExp(this.searchInput).split("").join("-?"), "i");
      

      但是没有原生的escapeRegExp方法。
      你可以在Escape string for use in Javascript regex找到一个
      lodash 库中还有一个 escapeRegExp 函数,如果它已经在您的依赖项列表中(避免您添加另一个函数)

      【讨论】:

        【解决方案3】:

        您可以创建一个函数来删除查询和城市字符串中的所有spaces-。如果城市包含查询,则将查询字符串拆分为最后一个字母,并获取该字母在查询中的出现次数。计算要切片的长度,返回原始城市字符串的匹配部分。

        const findMatch = (q, c) => {
          const query = q.toLowerCase().replace(/[\s-]/g, "");
          const city = c.toLowerCase().replace(/[\s-]/g, "");
          
          if (city.includes(query)) {
            const last = query.charAt(query.length - 1); // last letter
            const occ = query.split(last).length - 1; // get occurences
            // calculate slice length
            const len = c.toLowerCase().split(last, occ).join(" ").length + 1;
            return c.slice(0, len);
          }
        
          return "No matching city found."
        }
        
        const city = "Sint-Jan Test";
        console.log(findMatch("sint j", city));
        console.log(findMatch("sintj", city));
        console.log(findMatch("Sint Jan t", city));
        console.log(findMatch("sint-j", city));
        console.log(findMatch("Sint-J", city));
        console.log(findMatch("SintJan te", city));

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-11-20
          • 2014-05-08
          • 2021-01-02
          • 1970-01-01
          • 1970-01-01
          • 2020-12-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多