5

I'm a noobie that has been learning by his own and messing around with javascript and I stumbled upon that nightmare called 'regular expressions'...I kinda know a little bit about them and I been doing fancy stuff, but I'm stuck and I'd like you clarify this to me:

I've been reading and looking for a way to create matches and I tripped on with great answer:

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

var $rows = $('#table tr');
$('#search').keyup(function() {

var val = '^(?=.*\\b' + $.trim($(this).val()).split(/\s+/).join('\\b)(?=.*\\b') + ').*$',
    reg = RegExp(val, 'i'),
    text;

$rows.show().filter(function() {
    text = $(this).text().replace(/\s+/g, ' ');
    return !reg.test(text);
}).hide();

});

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

I kinda understand what is going on there but could somebody break that down and 'translate it in javascript' for me so I can catch the idea better, I barely can do cool stuff with jquery since I'm still studying javascript, I know certain things about the jqueries but not enough to fully understand what he did there and enough about regular expressions to know that the guy who wrote taht code is a genius <3

this is what i understand and please correct me:

var $rows = $('#table tr'); 

it's the scope, the 'target' in which is gonna be looking for the match

pd:that's the first time I see the'$' to declare variables and for what I've looked it sets it as a jQuery object..it's that right?

var val = '^(?=.*\\b' + $.trim($(this).val()).split(/\s+/).join('\\b)(?=.*\\b') + ').*$',
    reg = RegExp(val, 'i'),
    text;

the '$.trim($(this).val()' is equal to $.trim($("#user_input").val()); .......right?

reg = RegExp(val, 'i')

the reg variable works as a constructor to find the match that will be case-insensitive, but shouldn't it be 'reg = new RegExp(val, 'i')' or I can set as it is as well?

here is when I get confused the most

$rows.show().filter(function() {
        text = $(this).text().replace(/\s+/g, ' ');
        return !reg.test(text);
}).hide();

what I can understand is that the matches will be shown only if they pass the filter set by the text variable and the ones who don't will be hidden, I haven't the slightest idea of what the $(this) is equivalent to....in the text variable.. and from there on I have no idea what's going on, I found that .test() returns true or false when it finds a match in the regexp object but why does it have the ! at the beginning?

Elrond_EGLDer
  • 47,430
  • 25
  • 189
  • 180
nero
  • 97
  • 4
  • Mostly the regex string construction splits some string into words, then joins them together with an assertion. The resulting string is something like this `^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3).*$`, which is kind of missing a `\b` after word3. The regex itself is a validation that the `text` from `!reg.test(text);` does not contain _any_ of these (negation). Other than that, I don't know JS well enough. –  Dec 31 '14 at 22:20

3 Answers3

1

var $rows = $('#table tr');

it's the scope, the 'target' in which is gonna be looking for the match

pd:that's the first time i see the'$' to declare variables and for what I've looked it sets it as a jquery object..it's that right?

Yes, $rows is target, but the '$' of sign is actually meaningless, that is an approach of jquery programmers. It's good for remembering a jquery object, "hımmm, this has a '$' at the start, so that must be a jquery object".

In fact var $rows = $('#table tr'); and var rows = $('#table tr'); -> these are same, there aren't any differences between $rows and rows.

-----------------------------------------------------------------------------------------

var val = '^(?=.\b' + $.trim($(this).val()).split(/\s+/).join('\b)(?=.\b') + ').*$', reg = RegExp(val, 'i'),

text; the '$.trim($(this).val()' is equal to $.trim($("#user_input").val()); .......right?

In javascript this refers the event's owner. For the example that you shared, this used in keyup callback function of $('#search') so that means this refers the $("#search").

-----------------------------------------------------------------------------------------

reg = RegExp(val, 'i') the reg variable works as a constructor to find the match that will be case-insensitive, but shouldn't it be 'reg = new RegExp(val, 'i')' or I can set as it is as well?

There are nice explanations of new keyword for Javascript in this question, you can check it.

-----------------------------------------------------------------------------------------

$rows.show().filter(function() {
        text = $(this).text().replace(/\s+/g, ' ');
        return !reg.test(text);
}).hide();

Let's explain it step by step

var $rows = $('#table tr');
  • $rows is an array of tr objects
  • $rows.show() means show all tr tags which are in a table that's id is #table.
  • jQuery is chainable, that means $rows.show() returns $rows again
  • so $rows.show().filter() = $rows.filter
  • again $rows is still is an array of tr objects, so $rows.filter() loops through this object like a for loop, the callback function is processed for each objects in $rows.
  • In a callback function this refers the owner, in this example the owner is the object at that time while filter is looping the $rows.
  • As you said test() returns a bool value.
  • ! is an inverter,

    !true = false

    !false = true

Community
  • 1
  • 1
Okan Kocyigit
  • 12,633
  • 18
  • 64
  • 119
1

I'll have a go! Guten tag, nero!

I'll answer your questions from top to bottom and see if we can get it covered. First, you're understanding of the rows variable is correct. It's a jquery object that contains an array of DOM objects that match your search string. In this case, tr's within a table.

You've also got the #user_input part right. $(this) inside the keyup function is a reference to the DOM object that threw the event in the first place. In this case, the user input.

but shouldn't it be 'reg = new RegExp(val, 'i')' or I can set as it is as well?

Using the new keyword seems safer. For further refernce, see here: http://zeekat.nl/articles/constructors-considered-mildly-confusing.html. This may not be necessary if you're relying on just the 'static' bit of a class - but it's a better safe than sorry move I'd say.

Now for the hard part:

$rows.show().filter(function() {
        text = $(this).text().replace(/\s+/g, ' ');
        return !reg.test(text);
}).hide();

the $rows.show() is going to make visible all the DOM objects in the rows jquery object.

Then on that same set of objects it's going to 'filter' which means it's going to reduce the DOM objects that are in var rows based on a function that returns a boolean. In this case, that function is:

text = $(this).text().replace(/\s+/g, ' ');
return !reg.test(text);

which replaces all whitespace with a single space, and then tests if it matches your regular expression. So here, if it DOESN'T pass your regular expression (! = logical NOT) it stays in the set.

Lastly - this function will hide everything that passed the filter.

So it shows everything and then hides what doesn't match your regular expression.

Hope that helps! Habst ein guten neue Jahre (how's my German?)

LucasY
  • 26
  • 5
1

$ is the jQuery object, it's just a variable name that points to jQuery. Don't worry about $rows, that's just another variable name.

var $rows = $('#table tr'); 

The right-hand side is basically passing a selector to jQuery and telling it to find all DOM elements that match it. If you know CSS this is the same principle, #table is an element with id="table" and combined with tr means select all rows (tr being the table row html tag) within that element.

In pure javascript this could be written as

var $rows = document.querySelectorAll("#table tr");

The result is a list of elements.

Then you find another element, and attach an event listener:

$('#search').keyup(function() { ... });

Notice this is passing another selector to jQuery, which returns the desired element, onto which you attach a keyup event. In JavaScript this might be:

document.getElementById("search").addEventListener("keyup", function() { ... });

When that keyup event is triggered on the element the function is executed, and you are building a string val which contains:

... + $.trim($(this).val()).split(/\s+/).join('\\b)(?=.*\\b') + ...

this in $(this).val() evaluates to the element which was found by the #search selector, in this case an input field.

This might be the following in pure javascript:

... + document.getElementById("search").value.trim().split(/\s+/).join('\\b)(?=.*\\b') + ...

If you evaluate that expression, it 1) trims whitespace, 2) splits the result into an array of strings on every whitespace character and 3) joins that array with the delimiter \\b)(?=.*\\b

Steps 2) and 3) are basically a String.replace(/\s+/, '\\b)(?=.*\\b') but quicker.

Moving onto the last bit, the jQuery show() method is applied to each element in $rows, which was that list of elements (table rows) found at the beginning. This makes every row visible.

Then the filter method is applied to that list, this is a loop through the list of elements calling the function defined within on each element. Notice, this within the filter function now refers to the table row being tested, not the search field.

If that filter function returns true on a list item that item remains in the resulting list, if false it is removed. Here that prepared RegExp is applied, and if it matches the function returns false. So after filtering you have a list of elements/rows which do not match, and finally .hide() is applied which is a jQuery method to hide all elements on which it is called. So you hide the rows that don't match.

The code may look something like this in "pure" javascript (it should work now, I fixed the problem cjsmith wrote about in the comments).

var $rows = document.querySelectorAll("#table tr");
document.getElementById("search").addEventListener("keyup", function(e) {

  var val = '^(?=.*\\b' + e.target.value.trim().split(/\s+/).join('\\b)(?=.*\\b') + ').*$';
  var reg = RegExp(val, 'i');

  Array.prototype.forEach.call($rows, function(row) {
    var text = row.textContent.replace(/\s+/g, ' ');
    row.style.display = reg.test(text) ? 'table-row' : 'none';
  });

});

PS. Happy New Year!

Nenad Vukicevic
  • 603
  • 6
  • 12
  • 1
    Works great, I just had to change the display line to elem.style.display = reg.test(text) ? 'table-row' : 'none'; – james Dec 31 '14 at 23:01