【问题标题】:Highlight results when searching divs搜索 div 时突出显示结果
【发布时间】:2017-11-02 10:06:40
【问题描述】:

我有一个这样的 div 设置:

<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>

我允许用户使用以下命令搜索 div:

jQuery(document).ready(function() {
    jQuery("#search").on("keyup click input", function () {
        var val = jQuery(this).val();
        if (val.length) {
            jQuery(".entry").hide().filter(function () {
                return jQuery('.title, .description',this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
            }).show();
        }
        else {
            jQuery(".entry").show();
        }
    });
});

效果很好,试试jsFiddle

我的问题是,如何突出显示搜索词?比如用户搜索test,我想把文字test换成&lt;span&gt;标签。

编辑:请注意,我知道如何搜索/替换文本,但我似乎无法使用我的搜索功能使其正常工作。

【问题讨论】:

  • 我会说这个有点不同,当使用这样的搜索时。
  • 这个问题符合条件的时候我会奖励50分。

标签: javascript jquery


【解决方案1】:

优化方案

在 cmets 中讨论了所有问题并尝试优化解决方案以使其不会缺少任何最终错误之后,我重构了代码并对其进行了优化:

$(document).ready(function() {
  $("#search").on("keyup click input", function() {
    var val = jQuery(this).val();
    var regExp = new RegExp(val, 'ig');
    var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
    if (val.length) {
      $(".entry").hide().filter(function() {
        var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
        if (val.length > 3) {
          $('.title, .description', this).each(function(k, v) {
            if ($(v).text().match(regExp)) {
              $(v).html($(v).text().replace(regExp, '<span class="highlight">$&</span>'));
            } else {
              $(v).html($(v).text().replace(reg, '$&'));
            }
          });
        } else {
          $('.title, .description', this).each(function(k, v) {
            $(v).html($(v).text().replace(reg, '$&'));
          });
        }
        return found;
      }).show();
    } else {
      $('.title, .description').each(function(k, v) {
        $(v).html($(v).text().replace(reg, '$&'));
      });
      $(".entry").show();
    }
  });
});
.highlight {
  background-color: blue
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>

它循环遍历元素并使用带有匹配组的正则表达式,如果迭代的元素内容与正则表达式匹配,则将匹配的文本替换为包含在跨度中的相同内容,否则只需将内容设置为其原始形式。


原答案

你应该这样做:

    var val = jQuery(this).val();
    if (val.length) {
      $(".entry").hide().filter(function() {
        var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
        var regExp = new RegExp(val, 'ig');
        $('.title, .description', this).each(function(k, v) {
          if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
            var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
            $(v).html(newHTML);
          }
        });
        return found;
      }).show();
    } else {
      $(".entry").show();
    }

您需要遍历元素并使用带有匹配组的正则表达式,如果此元素内容与您的正则表达式匹配,则将匹配的文本替换为包含在跨度中的相同内容。

演示:

这是一个有效的演示:

$(document).ready(function() {
  $("#search").on("keyup click input", function() {
      var val = jQuery(this).val();
      if (val.length) {
        $(".entry").hide().filter(function() {
            var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
            var regExp = new RegExp(val, 'ig');
              $('.title, .description', this).each(function(k, v) {
                if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
                  var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
                  $(v).html(newHTML);
                }
              });
          return found;
        }).show();
    } else {
      $('.title, .description').each(function(k, v) {
        var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
        var newHTML = $(v).text().replace(reg, '$&');
        $(v).html(newHTML);
      });
      $(".entry").show();
    }
  });
});
.highlight {
  background-color: blue
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>

编辑:

这是一个仅在输入超过 2 个字母时才会突出显示句子的演示:

$(document).ready(function() {
  $("#search").on("keyup click input", function() {
    var val = jQuery(this).val();
    if (val.length) {
      $(".entry").hide().filter(function() {
        var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
        var regExp = new RegExp(val, 'ig');
        if (val.length > 2) {
          $('.title, .description', this).each(function(k, v) {
            if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
              var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
              $(v).html(newHTML);
            }
          });
        } else {
          $('.title, .description').each(function(k, v) {
            var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
            var newHTML = $(v).text().replace(reg, '$&');
            $(v).html(newHTML);
          });
        }
        return found;
      }).show();
    } else {
      $('.title, .description').each(function(k, v) {
        var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
        var newHTML = $(v).text().replace(reg, '$&');
        $(v).html(newHTML);
      });
      $(".entry").show();
    }
  });
});
.highlight {
  background-color: blue
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>

【讨论】:

  • 我问的原因是因为它搜索了的数据。因此,当用户开始输入第一个字母时,我们有很多匹配项,使浏览器几乎崩溃。另一种解决方案是仅在用户键入 3 个字符时突出显示术语。这可能吗? div 的显示/隐藏应该仍然发生,只有在输入 3 个字母后突出显示才会生效。
  • 感谢您的更新,虽然当字符数少于 2 时,脚本会因大量数据而变得异常缓慢。请参阅我的原始问题中的代码,该代码显示/隐藏 div,并且在处理大量数据时效果惊人。 是如果我们少于 2 个字符时我要执行的代码。我们还需要剥离 span 标签。我说得有道理吗?有点谜。
  • @HenrikPetterson 嗯,我明白了,我会尝试找到解决方案。
  • 有趣的方法。我进行了一些速度测试,比较了所有解决方案,这个测试表现最好。考虑使用&lt;mark&gt; 标签。也注意到了“错误”。
  • @chsdk 除了这个问题,这是迄今为止最完整的jQuery解决方案。当它符合条件时,我将奖励这 200 点并奖励它。它所缺少的只是某种形式的方法来解决这个特定问题。
【解决方案2】:

尝试使用contains(text) 而不是filter()。最初隐藏All div。然后仅显示包含div 的文本。并使用new RegExp() 将span 元素应用于子项中的匹配字母

对于在正则表达式中忽略区分大小写的匹配 ig 并且还为 contains 添加了不区分大小写的代码

更新在儿童上使用.title, .description 修复

jQuery(document).ready(function() {
  jQuery("#search").on("input", function() {
    var val = jQuery(this).val()
    jQuery(".entry").hide()
    jQuery(".entry:contains(" + val + ")").show()
    jQuery(".entry").each(function() {
      if ($(this).find(".title, .description:contains(" + val + ")")) {
        $(this).find(".title, .description:contains(" + val + ")").html(function() {
          return $(this).text().replace(new RegExp('('+val+')', 'ig'), '<span>$1</span>')
        })
      }
    })
  });

})
jQuery.expr[':'].contains = function(a, i, m) {
  return jQuery(a).text().toUpperCase()
      .indexOf(m[3].toUpperCase()) >= 0;
};
.entry {
  background: #fff;
  margin-bottom: 10px;
  width: 300px;
}

span {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">

<div class="entry">
  <div class="title">hello world test 123</div>
  <div class="description">lorem ipsum test test1 testing</div>
</div>

<div class="entry">
  <div class="title">Attack on titan</div>
  <div class="description">fullmetal alchemist</div>
</div>
<div class="entry">
<div>
  <div class="title">For nested element on titan</div>
  <div>
  <div class="description">fullmetal alchemist nested</div>
  </div>
  </div>
</div>

【讨论】:

  • 这太棒了。虽然我的目标是 .title, .description 而不是所有的儿童 div。您能否调整代码以反映这一点?符合条件时我会奖励赏金。
  • 尝试输入“testing”,它仍然会在.title 中突出显示“test”。
  • 感谢您的回答,虽然它现在似乎只与.description 匹配。我认为您在那里使用contain() 的方式存在问题。
  • 啊,我明白了问题所在。您的代码不会检测 div 是否嵌套。例如,尝试将描述元素包装到像这样&lt;div&gt;&lt;div class="description"&gt;fullmetal alchemist&lt;/div&gt;&lt;/div&gt; 的 div 中
  • 项目符号样式和链接被删除,如何解决这个问题
【解决方案3】:

试试这个:

document.getElementById('search').onkeyup = userInput;
document.getElementById('search').onclick = userInput;
document.getElementById('search').oninput = userInput;
var allEntries = document.querySelectorAll('.entry');

function userInput () {
    var val = this.value;
    for (var i = 0; i < allEntries.length; i++) {
        var entryElement    = allEntries[i];
        var title           = entryElement.querySelector('.title');
        var description     = entryElement.querySelector('.description');
        var noHtmlSearchStr = '';
        if (title)       noHtmlSearchStr +=       title.innerText;
        if (description) noHtmlSearchStr += description.innerText;
        if (noHtmlSearchStr.length > 0) {
            if (noHtmlSearchStr.toLowerCase().indexOf(val.toLowerCase()) != -1) {
                // Remove existing <b> tags.
                var regexp1 = new RegExp('(<b>|<\/b>)', 'gi');
                if (title)             title.innerHTML =       title.innerHTML.replace(regexp1, '');
                if (description) description.innerHTML = description.innerHTML.replace(regexp1, '');

                if (val.length > 3) {
                    var regexp2 = new RegExp('(' + val + ')(?!>)', 'gi');
                    if (title)             title.innerHTML =       title.innerHTML.replace(regexp2, '<b>$1</b>');
                    if (description) description.innerHTML = description.innerHTML.replace(regexp2, '<b>$1</b>');
                }
                entryElement.style.display = 'block';
            } else {
                entryElement.style.display = 'none';
            }
        }
    }
}
.entry {
    background: #fff;
    margin-bottom: 10px;
    width: 300px;
}
<input id="search">

<div class="entry">
    <div class="title">hello world test 123</div>
    <div class="description">div lorem <span>ipsum</span> test <div>test1</div> testing span</div>
</div>

<div class="entry">
    <div class="title">attack on titan</div>
    <div class="description">fullmetal alchemist</div>
</div>

<div class="entry"></div>

<div class="entry">
    <div class="title">attack on titan</div>
</div>

<div class="entry">
    <div class="description">Let's not go to Camelot, 'tis a silly place</div>
</div>

JS代码说明

  1. 将所有事件绑定到userInput() 函数。
  2. 使用.entry 类获取所有元素并将它们存储在allEntries 中。
  3. 获取用户的输入并存储在val
  4. 遍历allEntries
  5. titledescription 中获取要搜索的文本并存储在noHtmlSearchStr 中。
  6. 如果val 匹配noHtmlSearchStr 的某些部分,则显示entryElement,否则隐藏它。
  7. titledescription 中删除&lt;b&gt; 标签。
  8. 如果用户搜索的长度 (val) 超过三个字符,则突出显示文本上的匹配项,否则不突出显示任何内容。

【讨论】:

  • 这是非常好的大卫。您是否有机会添加一些小文本来描述您的方法?
  • 好的,我现在就这样做@HenrikPetterson
  • 这个有问题。如果您尝试全部大写,它将以全部大写显示结果,而不像原始文本那样显示。如果我没有正确解释这一点,请告诉我。
  • 我问的原因是因为它搜索了的数据。因此,当用户开始输入第一个字母时,我们有很多匹配项,使浏览器几乎崩溃。另一种解决方案是仅在用户键入 3 个字符时突出显示术语。这可能吗? div 的显示/隐藏应该仍然发生,只有在输入 3 个字母后突出显示才会生效。
  • 当然是@HenrikPetterson。只需使用entryElement.querySelector('.title')。请参阅我的更新答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-07
  • 2010-09-16
  • 2015-09-09
  • 2014-03-07
  • 2014-07-25
  • 1970-01-01
相关资源
最近更新 更多