JQuery:用html替换字符串中的多个子字符串实例,没有错误

时间:2016-10-19 08:21:55

标签: javascript jquery html regex replace

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

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

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

但是,这只排除'<'之间包含的文字。和'<'。

如果您在所包含的工作小提琴中输入:'a level a level',那么您将看到'a levels'作为下拉框返回,但'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>

1 个答案:

答案 0 :(得分:1)

你的bug起源于正则表达式,以及你使用它的方式:

onload

您似乎已经找到的问题是,在您使用var regEx = new RegExp("(" + searchWord + ")(?!([^<]+)?>)", "gi") 替换"a levels"之后,此模式仍然匹配循环的未来迭代(即,对于"...toggle='dropdown'>a levels<span class='caret'>...") - 这搞砸了生成的HTML。

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

"a level"

然而,这也不起作用 - 并且仅用于掩盖原始错误。现在,只要任何模式匹配,您就会退出 - 这就是for (var itr = 0; itr < strings_used.length; ++itr) { if (strings_used[itr].indexOf(i) !== -1) { return true; } } "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>

然而,更清晰的解决方案是一次性执行查找和替换 - 从而避免了“搜索不在HTML元素中的字符串”的需要第一名。正如对你正确复制的解决方案的帖子的评论指出的那样,它是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>"
    )
  );
});