【问题标题】:JQuery: Replace multiple instances of substring within string with html without errorsJQuery:用html替换字符串中子字符串的多个实例而没有错误
【发布时间】:2016-10-19 08:21:55
【问题描述】:

我希望在一个字符串中找到一个子字符串(从一个数组中),然后用一个标题等于子字符串的下拉框替换它。

字符串来自用户输入,子字符串是从我的工作代码中的数据库中提取的。

我根据 DavidTonarini 在这个问题中给出的答案工作:Javascript: replace() all but only outside html tags

但是,这仅排除包含在 '

如果您在包含的工作小提琴中输入:“a levels a level”,那么您将看到“a levels”作为下拉框返回,但“a level”作为纯文本返回,但它应该是与它在数组中的条目匹配并替换为下拉框。在用户输入中重复相同的字符串时也会出现问题。我希望能够在用户输入中多次匹配相同的子字符串。

var data = {
  "a_levels": {
    "a_level": {
      id: 1,
      units: 2,
      created: "2016-10-04 19:00:05",
      updated: "2016-10-05 09:37:46"
    },
    "a_levels": {
      id: 2,
      units: 2,
      created: "2016-10-05 08:19:27",
      updated: "2016-10-05 09:37:39"
    }
  },
  "a_level": {
    "a_level": {
      id: 1,
      units: 2,
      created: "2016-10-04 19:00:05",
      updated: "2016-10-05 09:37:46"
    },
    "a_levels": {
      id: 2,
      units: 2,
      created: "2016-10-05 08:19:27",
      updated: "2016-10-05 09:37:39"
    }
  }
};
var input, // Create empty variables.
  response;

$('#submit').click(function() {
  input = $('#userInput').val();
  response = input;
  // CREATE DROPDOWN BOXES.
  var strings_used = [];
  $.each(data, function(i, v) { // Iterate over first level of output.

    for (var itr = 0; itr < strings_used.length; ++itr) {
      if (strings_used[itr].indexOf(i) !== -1) {
        return true;
      }
    }
    var searchWord = i.replace(/_/g, " "); // Replace underscores in matches with blank spaces.
    var regEx = new RegExp("(" + searchWord + ")(?!([^<]+)?>)", "gi"); // Create regular expression which searches only for matches found outside html tags.
    var tmp = response.replace(regEx, "<span class='btn-group'><button class='btn btn-primary dropdown-toggle' type='button' data-toggle='dropdown'>" + searchWord + "<span class='caret'></span></button><ul class='" + i + " dropdown-menu'></ul></span>"); // Replace matching substrings with dropdown boxes.
    if (tmp !== response) { // Check if replacement is complete.
      response = tmp; // Update response.
      strings_used.push(i);
    }
  });
  $('#template').empty().append(response); // Populate template container with completed question response including dropdown boxes.
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>

  <div id="searchbox">
    <div class="input-group">
      <input id="userInput" type="text" class="form-control" placeholder="type here...">
      <span id="submit" class="input-group-btn">
			  <button class="btn btn-default" type="submit">GO!</button>
			</span>
    </div>
  </div>

  <div class="row">
    <div id="template" class="col-sm-10 col-md-offset-1 text-left"></div>
  </div>

</body>

【问题讨论】:

    标签: javascript jquery html regex replace


    【解决方案1】:

    您的错误源自正则表达式,以及您使用它的方式:

    var regEx = new RegExp("(" + searchWord + ")(?!([^<]+)?>)", "gi")
    

    问题,正如您自己似乎已经发现的那样,在您将 "a levels" 替换为 "...toggle='dropdown'&gt;a levels&lt;span class='caret'&gt;..." 之后,此模式仍然匹配循环的未来迭代(即,对于 @ 987654329@) - 搞砸了生成的 HTML。

    您尝试通过添加补丁来解决此问题:

    for (var itr = 0; itr < strings_used.length; ++itr) {
      if (strings_used[itr].indexOf(i) !== -1) {
        return true;
      }
    }
    

    但是,这也不起作用 - 只会掩盖原始错误。现在,只要 any 模式匹配,您就会退出 - 这就是为什么 "a level" 甚至不会被搜索到,如果 "a levels" 匹配。

    在不完全改变您的方法工作方式的情况下,这是一个快速补丁 - 我只是删除了您的 strings_used 逻辑并将正则表达式替换为:

    var regEx = new RegExp("(\\b" + searchWord + "\\b)(?!<)", "gi");
    

    var data = {
      "a_levels": {
        "a_level": {
          id: 1,
          units: 2,
          created: "2016-10-04 19:00:05",
          updated: "2016-10-05 09:37:46"
        },
        "a_levels": {
          id: 2,
          units: 2,
          created: "2016-10-05 08:19:27",
          updated: "2016-10-05 09:37:39"
        }
      },
      "a_level": {
        "a_level": {
          id: 1,
          units: 2,
          created: "2016-10-04 19:00:05",
          updated: "2016-10-05 09:37:46"
        },
        "a_levels": {
          id: 2,
          units: 2,
          created: "2016-10-05 08:19:27",
          updated: "2016-10-05 09:37:39"
        }
      }
    };
    var input, // Create empty variables.
      response;
    
    $('#submit').click(function() {
      input = $('#userInput').val();
      response = input;
      // CREATE DROPDOWN BOXES.
      $.each(data, function(i, v) { // Iterate over first level of output.
    
      // ** REMOVED: **
      //  for (var itr = 0; itr < strings_used.length; ++itr) {
      //    if (strings_used[itr].indexOf(i) !== -1) {
      //      return true;
      //    }
      //  }
    
        var searchWord = i.replace(/_/g, " "); // Replace underscores in matches with blank spaces.
    
        // ** CHANGED: **
        var regEx = new RegExp("(\\b" + searchWord + "\\b)(?!<)", "gi");
    
        var tmp = response.replace(regEx, "<span class='btn-group'><button class='btn btn-primary dropdown-toggle' type='button' data-toggle='dropdown'>" + searchWord + "<span class='caret'></span></button><ul class='" + i + " dropdown-menu'></ul></span>"); // Replace matching substrings with dropdown boxes.
        if (tmp !== response) { // Check if replacement is complete.
          response = tmp; // Update response.
        }
      });
      $('#template').empty().append(response); // Populate template container with completed question response including dropdown boxes.
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <body>
    
      <div id="searchbox">
        <div class="input-group">
          <input id="userInput" type="text" class="form-control" placeholder="type here...">
          <span id="submit" class="input-group-btn">
            <button class="btn btn-default" type="submit">GO!</button>
          </span>
        </div>
      </div>
    
      <div class="row">
        <div id="template" class="col-sm-10 col-md-offset-1 text-left"></div>
      </div>
    
    </body>

    然而,一个更简洁的解决方案是一次性执行查找和替换 - 从而避免在第一名。正如对您复制其解决方案的帖子的评论所指出的那样,它是a bad idea to parse HTML with regex——正是由于这种情况!

    我将把这个作为练习留给你尝试,但基本上我建议你的代码只搜索:

    var regEx = new RegExp("\\b(a level|a levels)\\b", "gi");
    

    并替换为:

    ... data-toggle='dropdown'>$1<span class='caret'> ...
    

    编辑:如下所述,这是一个更短、更简单且无错误的实现的可能草图:

    $('#submit').click( function() {
      var input = $('#userInput').val();
      var regEx = new RegExp("\\b(" +  Object.keys(data).join('|').replace(/_/g, " ") + ")\\b", "gi");
      $('#template').html(
        input.replace(
          regEx,
          "<span class='btn-group'><button class='btn btn-primary dropdown-toggle' type='button' data-toggle='dropdown'>$1<span class='caret'></span></button><ul class='" + "$1".replace(/ /g, "_") + "' dropdown-menu'></ul></span>"
        )
      );
    });
    

    【讨论】:

    • 谢谢。我看过并相信这是正确的,尽管我只能有限地访问互联网进行测试。现在就去尝试一下,很快就会更新。我想我只是需要练习更多的正则表达式。
    • 哇哇哇哇,等一下……我强烈建议您按照我的建议重构代码,而不是仅仅使用我的代码补丁版本并尝试对其进行调整以适应更复杂的需求.这是一个让你走上正轨的速写:jsfiddle.net/huwwefa0/15
    • 这是一个更接近您需要的版本.... 对干净编写的代码进行更改要容易得多 :) jsfiddle.net/huwwefa0/16
    • 谢谢伙计,虽然我不确定这是正确的方向。字符串必须按长度降序替换。这是一个目前被打破的尝试。不确定我到底做错了什么,但我认为我只需要不同的逻辑来解决问题:jsfiddle.net/huwwefa0/19。为了提高清晰度,我在“数据”数组中添加了一个额外的条目。
    • 采用我上面的解决方案,我用半行代码实现了您的附加要求;)jsfiddle.net/huwwefa0/20 ...我希望我在您使用的 A Level 资源中得到一些认可哈哈
    猜你喜欢
    • 2016-03-24
    • 2013-10-26
    • 2011-09-29
    • 1970-01-01
    • 2016-09-10
    • 1970-01-01
    • 2017-12-06
    • 1970-01-01
    相关资源
    最近更新 更多