2

I came across this pen at codePen, and found a bug. If you type in the following chars in the searchbox:

), (, [, +, \, *, ?

I get the following error (I entered each char separately):

enter image description here

I think the problem is at the regular expression part. Line 19:

var rgx = new RegExp(this.value, "i");

Why does this happen and how can I fix it?

CodePen

document.addEventListener("DOMContentLoaded", function() {
  "use strict"
  var style = "" + "<style>" + ".filter .hidden {" + "opacity: 0;" + "}" +
    ".filter > * {" + "position: absolute;" +
    "transition: .5s ease-in-out;" + "}" + "</style>";
  document.head.insertAdjacentHTML("beforeend", style);

  var list = document.querySelectorAll(".filter > *");
  var h = list[0].offsetHeight,
    arr = [],
    i = -1,
    l = list.length;
  var anim = "transform" in document.body.style ? "transform" : "webkitTransform";

  while (++i < l) {
    arr.push(list[i].textContent.trim());
    list[i].style[anim] = "translateY(" + i * h + "px)";
  }

  document.querySelector("input.filter").addEventListener("input", function() {
    var rgx = new RegExp(this.value, "i");
    arr.forEach(function(el, idx) {
      if (rgx.test(el)) list[idx].classList.remove("hidden");
      else list[idx].classList.add("hidden");
      var i = -1;
      var p = 0;
      while (++i < l) {
        if (list[i].className != "hidden")
          list[i].style[anim] = "translateY(" + p++ * h + "px)";
      }
    })
  })
})
@import url(https://fonts.googleapis.com/css?family=Titillium+Web);
 html {
  height: 100%;
}
body {
  width: 100%;
  height: 100%;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
  -ms-flex-pack: center;
  justify-content: center;
  margin: 0;
  font: 14px'Titillium Web', sans-serif;
  letter-spacing: 2px;
  background: -webkit-linear-gradient(320deg, #543958 0%, #a06060 25%, #bea27b 50%, #9ca898 75%, #506d8d 100%) no-repeat center center fixed;
  background: linear-gradient(130deg, #543958 0%, #a06060 25%, #bea27b 50%, #9ca898 75%, #506d8d 100%) no-repeat center center fixed;
}
section {
  -webkit-align-self: top;
  -ms-flex-item-align: top;
  align-self: top;
  margin: 150px 0;
  display: block;
  position: relative;
  width: 380px;
  max-height: 550px;
  color: rgba(244, 230, 254, 0.7);
  background: rgba(199, 131, 252, 0.05);
  border-top: 15px solid rgba(0, 0, 0, 0.15);
  border-bottom: 40px solid rgba(0, 0, 0, 0.06);
  -webkit-filter: drop-shadow(26px 26px 20px rgba(0, 0, 0, 0.5));
  filter: drop-shadow(26px 26px 20px rgba(0, 0, 0, 0.5));
  box-shadow: -4vh -6vh 16vh -6vh rgba(0, 0, 0, 0.2), -4vh 6vh 16vh -6vh rgba(0, 0, 0, 0.15);
}
section header {
  padding: 1em 1.4em;
  background: rgba(0, 0, 0, 0.12);
  margin: 0 auto 0 auto;
}
section header h4 {
  height: 3rem;
  margin: 0 0 1rem 0;
  padding: 0;
  line-height: 1.1rem;
  text-align: center;
  border-radius: 3px;
  font-size: 1.1rem;
}
section header h4 span {
  padding: 1.2rem;
}
section header h4 span.c {
  padding: .5rem;
  background: rgba(0, 0, 0, 0.045);
  border-bottom: 2px solid rgba(233, 205, 254, 0.8);
}
section header input[type="search"] {
  margin: 0 .5rem;
  padding: .5rem;
  width: 95%;
  color: rgba(244, 230, 254, 0.8);
  background: transparent;
  border: none;
  border-bottom: 1px solid rgba(210, 158, 250, 0.5);
  font-size: 1.15rem;
  -webkit-transition: translateX 6s ease-in;
  transition: translateX 6s ease-in;
}
section header input[type="search"]:focus {
  outline: none;
  border: 1px solid rgba(210, 158, 250, 0.5);
}
section header input[type="search"]:focus::-webkit-input-placeholder {
  -webkit-transform: translateX(70%);
  transform: translateX(70%);
  opacity: 0;
}
section header input[type="search"]:focus::-webkit-input-placeholder:-moz-placeholder {
  -webkit-transform: translateX(70%);
  transform: translateX(70%);
  opacity: 0;
}
section header input[type="search"]:focus::-webkit-input-placeholder::-moz-placeholder {
  -webkit-transform: translateX(70%);
  transform: translateX(70%);
  opacity: 0;
}
section header input[type="search"]:focus::-webkit-input-placeholder:-ms-input-placeholder {
  -webkit-transform: translateX(70%);
  transform: translateX(70%);
  opacity: 0;
}
section header input[type="search"]:focus::-webkit-input-placeholder::-ms-input-placeholder {
  -webkit-transform: translateX(70%);
  transform: translateX(70%);
  opacity: 0;
}
section header input[type="search"]::-webkit-input-placeholder {
  color: rgba(233, 205, 254, 0.8);
  -webkit-transition: ease-in 0.3s;
  transition: ease-in 0.3s;
  -webkit-transform-origin: 0 50%;
  transform-origin: 0 50%;
}
section header input[type="search"]:-moz-placeholder {
  color: rgba(233, 205, 254, 0.8);
  -webkit-transition: ease-in 0.3s;
  transition: ease-in 0.3s;
  transform-origin: 0 50%;
}
section header input[type="search"]::-moz-placeholder {
  color: rgba(233, 205, 254, 0.8);
  -webkit-transition: ease-in 0.3s;
  transition: ease-in 0.3s;
  transform-origin: 0 50%;
}
section header input[type="search"]:-ms-input-placeholder {
  color: rgba(233, 205, 254, 0.8);
  -webkit-transition: ease-in 0.3s;
  transition: ease-in 0.3s;
  transform-origin: 0 50%;
}
section header input[type="search"]::-ms-input-placeholder {
  color: rgba(233, 205, 254, 0.8);
  -webkit-transition: ease-in 0.3s;
  transition: ease-in 0.3s;
  transform-origin: 0 50%;
}
section .filter {
  padding: 1rem 0;
}
section .filter li {
  width: 100%;
  padding: 1rem 0;
  line-height: 1.5rem;
  -webkit-filter: drop-shadow(26px 26px 20px rgba(0, 0, 0, 0.7));
  filter: drop-shadow(26px 26px 20px rgba(0, 0, 0, 0.7));
  list-style-type: none;
  text-align: left;
}
section .filter li span {
  font-size: 1.15rem;
  padding: 1.8rem;
}
section .filter li:nth-child(odd) {
  background: rgba(255, 255, 255, 0.03);
  margin: 0;
}
<section>
  <header>
    <h4>
      <span>Log</span>
      <span>Favorites</span>
      <span class ='c'>Contacts</span>
  </h4>
    <input type='search' placeholder=" Search" autofocus class="filter">
  </header>
  <ul class="filter">
    <li><span class='img'></span><span class='name'>John</span>  <span class='ph'>609-579-1254</span>
    </li>
    <li><span class='img'></span><span class='name'>Diane</span>  <span class='ph'>908-240-2297</span>
    </li>
    <li><span class='img'></span><span class='name'>Mike</span>  <span class='ph'>303-539-1425</span>
    </li>
    <li><span class='img'></span><span class='name'>Mary</span>  <span class='ph'>424-308-9976</span>
    </li>
    <li><span class='img'></span><span class='name'>Adam</span>  <span class='ph'>509-998-0025</span>
    </li>
    <li><span class='img'></span><span class='name'>Billy</span>  <span class='ph'>609-898-3325</span>
    </li>
  </ul>
</section>
Horay
  • 1,288
  • 14
  • 31
  • See http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex – elclanrs Feb 21 '16 at 23:24
  • 2
    Strangely, these `), (, [, +, \, *, ?` all look like metacharacters. When you want to search for them as _literals_, they have to be escaped. `\), \(, \[, \+, \\, \*, \?` –  Feb 21 '16 at 23:33
  • @sln Can you please show me how to do that? – Horay Feb 21 '16 at 23:36
  • @Horay he did. use a backslash in front if it. `\)` to give you a `)`. – mariocatch Feb 21 '16 at 23:37
  • @mariocatch Oh. But then how can I implement that in my code? Do I do this: `var rgx = new RegExp(this.value, "/\), \(, \[, \+, \\, \*, \?/i");` ? – Horay Feb 21 '16 at 23:42

2 Answers2

2

This might be a good way to replace all the metachars to make them literal's.

text_escaped = text.replace(/([.^$|*+?()\[\]{}\\-])/g, "\\$1");

Update

After looking at your codepen link, I saw that you were not instantiation
the regex objcect correctly.

The parameters are as follows: RegExp( string, flags )
I believe you were using this wrong.
Yours var rgx = new RegExp(this.value, text_escaped);
Should have been var rgx = new RegExp(text_escaped);

Here are a couple of useful links about JavaScript regex:

Regex Reference's

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions http://www.w3schools.com/jsref/jsref_obj_regexp.asp


From there it shows how you can define your own function to escape metacharacters.

Escaping user input to be treated as a literal string within a regular expression can be accomplished by a simple replacement:

function escapeRegExp(string){
  return string.replace(/([.*+?^${}()|[\]\\])/g, "\\$1"); // $1 means capture group 1
}

Then a usage example is something like:

var text_to_find = "<[SBG]>";
var findRx = new RegExp( escapeRegExp( text_to_find ) );

// Use findRx here ..
  • Thanks! Then I do `var rgx = new RegExp(text_escaped, "i");`? – Horay Feb 22 '16 at 00:16
  • This `/[.^$|*+?()\[\]{}\\-]/g` instantiates a new RegExp for you. The replace function uses it. The text_escaped is what you send to be searched. So `text` entered, `text_escaped` used in the search. –  Feb 22 '16 at 00:39
  • Since there may be other characters I didn't test that can also throw an error, I was thinking to have a regex that removes all chars besides for: `a-z, A-Z, 0-9, '#'`. What this be a good way of doing that? `var rgx = new RegExp(this.value, "i"); rgx = rgx.replace(/[^a-z0-9\#]/gi,'');`? – Horay Feb 22 '16 at 21:31
  • @Horay - The metachars listed are the only ones that could throw an exception. After they're escaped, the whole string is a literal. –  Feb 23 '16 at 23:34
  • Thanks! I tried the following, but it didn't work: `var text_escaped = this.value.replace(/[.^$|*+?()\[\]{}\\-]/g, "\\$1"), rgx = new RegExp(this.value, text_escaped);` – Horay Feb 24 '16 at 00:17
  • I forgot to include the capture group, use this `text_escaped = text.replace(/([.^$|*+?()\[\]{}\\-])/g, "\\$1");` –  Feb 24 '16 at 00:33
  • I tried what you said: ` var text_escaped = this.value.replace(/([.^$|*+?()\[\]{}\\-])/g, "\\$1"), rgx = new RegExp(this.value, text_escaped);` and I still get the following error: "Uncaught SyntaxError: Invalid flags supplied to RegExp constructor 'a'"` – Horay Feb 24 '16 at 00:37
  • Try printing out the results of `text_escaped` and report it's findings. –  Feb 24 '16 at 00:49
  • It prints out what I input. Here's an updated pen: http://codepen.io/anon/pen/pgMLwZ?editors=0010 – Horay Feb 24 '16 at 00:54
  • @Horay - I updated my answer describing what you did wrong. Basically, you did not instantiate (create) the object correctly. The prototype is `RegExp( string, flags )` where _flags_ is a string as well, and optional. I tried changing your codepen to correct the error, but I don't think it had any affect. I did an alert on your `this.value` but it returns an empty string. –  Feb 24 '16 at 16:24
  • Now it doesn't take into account the first char of a word. For example, when I type 'J' for Joun, everything disappears. http://codepen.io/anon/pen/YqKKEB?editors=0010 – Horay Feb 25 '16 at 17:07
  • You probably want to make it case independent `RegExp( text_escaped, 'i' )` Works for me http://codepen.io/anon/pen/MygQee?editors=0010 and it just uses `Test` so the input can exist anywhere in your array var. Btw, if you don't vote me up, I'm deleting this. –  Feb 25 '16 at 17:47
  • Omg, relax dude! lol. I thought I already accepted your answer. Gonna do em both now. Accept and upvote! – Horay Feb 25 '16 at 18:26
  • 1
    And it finally works! Thanks a million! Really appreciate the time, patience and work you've done! – Horay Feb 25 '16 at 18:34
1

The answer to this question requires a familiarity with Regular Expressions. From http://www.regular-expressions.info:

A regular expression (regex or regexp for short) is a special text string for describing a search pattern. You can think of regular expressions as wildcards on steroids. You are probably familiar with wildcard notations such as *.txt to find all text files in a file manager. The regex equivalent is .*\.txt$.

Regular expressions are put together using a variety of special characters, all of which have different meanings. The characters that, as you discovered, cause the search box to break are considered special characters in regular expressions. For example, ( and ) are used to indicate "groups" of characters within the regular expression.

To briefly answer the question of "how to fix it": the easy way would be to add a \ in front of any special characters in this.value. \ is a special character which causes the character that immediately follows it to be considered as a "non-special" character.

e.g.

var escapedValue = this.value.replace(")", "\\)");
escapedValue = escapedValue.replace("(", "\\(");
escapedValue = escapedValue.replace("[", "\\[");
escapedValue = escapedValue.replace("+", "\\+");
escapedValue = escapedValue.replace("\\", "\\\\)");
escapedValue = escapedValue.replace("*", "\\*");
escapedValue = escapedValue.replace("?", "\\?");
var rgx = new RegExp(escapedValue, "i");

Note that this code snippet won't completely fix the problem (the list of special characters isn't exhaustive), and it's not a very elegant solution, but it illustrates the general concept.

Ryan Biwer
  • 160
  • 1
  • 10
  • Thanks! Very well explained! How can I make it more elegant, and 'replace' **all** special chars? – Horay Feb 21 '16 at 23:46
  • I'd check out this stackoverflow post: http://stackoverflow.com/questions/3115150/how-to-escape-regular-expression-special-characters-using-javascript – Ryan Biwer Feb 21 '16 at 23:56
  • Thanks! What if I want to allow only these in the regex, "`a-z, A-Z, 1-9, #`" How can I do that? Would I do this: `var rgx = new RegExp(this.value, "/a-z,1-9, #i");`? – Horay Feb 22 '16 at 00:07