6

I've gathered an Array (I think) of required form elements, and have added 'blur' listener.

    var formInputs = $(':input').filter('[required]');
  formInputs.each(function(i) {
    $(this).on('blur', function() { // Each time we leave a 'required' field, check to see if we can activate the 'submit' button.
      submitEnabler(formInputs);
    });
  });

So, once someone has left one of these fields, I want to run through this array using .every() and check if the fields are valid - that is if they have a 'success' class that I have defined.

function isValid(input) {
  return input.hasClass('is_glowing_success');
}

function submitEnabler(inputs) {

  console.log(inputs.every(isValid));
}

I keep getting back:

Uncaught TypeError: inputs.every is not a function
    at submitEnabler

Now, I could do something like this...

for (var i = 0; i < inputs.length; i++) {
    if ($(inputs[i]).hasClass('is_glowing_success')) {
      console.log('yes');
    } else {
      console.log('no');
    }
  }

But, why can't I just use: Array.Prototype.every() ?

CodeFinity
  • 1,142
  • 2
  • 19
  • 19
  • 1
    To improve code, you can write `$(':input[required]').blur(submitHandler);` and make changes in `submitHandler` to access `inputs`. – Tushar Apr 29 '17 at 17:35
  • @Tushar I did: $(':input[required]').blur(submitHandler(formInputs)); Works great! Thanks! The previous code did feel a bit 'icky.' – CodeFinity Apr 29 '17 at 17:54
  • 2
    It should be `.blur(function() { submitHandler(formInputs); });`. Otherwise, the handler will be called immediately and not on `blur` event.s – Tushar Apr 29 '17 at 17:58
  • Absolutely correct. I got that figured out now, and makes sense. – CodeFinity Apr 29 '17 at 19:14

3 Answers3

5

Because jQuery objects have no every method, and formInputs is a jQuery object.

If you want an array instead, call get() to get one.

I've gathered an Array (I think) of required form elements...

No, it's just jQuery object. jQuery objects are very array-like, but they aren't arrays. Worse, they have some array-like methods (such as filter and map) that call their callbacks with different arguments than the equivalent Array.prototype methods.

In isValid, you'd need to handle the fact you're now dealing with a raw DOM element, which means either wrapping it with a jQuery object and using hasClass:

function isValid(input) {
  return $(input).hasClass('is_glowing_success');
}

or using the DOM's classList:

function isValid(input) {
  return input.classList.contains('is_glowing_success');
}

That latter works on all modern browsers, but not all older ones. However, it can be polyfilled on older browsers. More about that on MDN.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Ah, so using get() might *get* me the Array I am after so that I can use that method...ok, will try after some time. Thanks. – CodeFinity Apr 29 '17 at 17:36
  • 1
    @guest271314 I was thinking the same thing, but the nice thing about `.every()` is that it stops looping as soon as it encounters a failure, while `.filter` has to check every element and build a new jQuery collection. – Barmar Apr 29 '17 at 18:13
  • @Barmar `$.each()` and `.each()` offer mechanism to stop iteration with `return false` https://jsfiddle.net/Ldnbehy7/ ; have rarely used the pattern, here. – guest271314 Apr 29 '17 at 18:15
  • @guest271314 But `$.each` doesn't return a useful result, it just returns the original collection (useful for chaining, but not for telling whether the tests were succesful) – Barmar Apr 29 '17 at 18:21
  • @Barmar Yes, one reason why have not used pattern frequently. Would use same approach described for `.filter()` pattern; that is, increment a variable from `0` to `N`, check if `N` is equal to `.length` of collection at line following `$.each()`. Or define a `Boolean`, if condition is met within `$.each()` callback set `Boolean` to the opposite of existing defined `Boolean`, call `return false`, then check `Boolean` variable value at line following `$.each()`. It could also be composed as `return (bool = )` or `return (bool = )` – guest271314 Apr 29 '17 at 18:24
  • @Barmar That would be a useful `$.fn` method or function defined at `$` object iteself. That is a jQuery method which break when condition is met and returns a `Boolean` with `context` set to original selector, or capable of being set to a specific selector or object; or original object or selector set to expected parameter of callback function within method. How to phrase such a Question? Is the above sufficient? – guest271314 Apr 29 '17 at 18:29
  • @guest271314 Context can't be returned by a function in JS. – Barmar Apr 29 '17 at 18:31
  • @Barmar `Reflect.apply()` or `jQuery.proxy()` can set the context of a function call. Unless using the term `context` inappropriately, above? – guest271314 Apr 29 '17 at 18:32
  • @guest271314 That's not the same as a function returning a context. When you chain functions, the context of the next function is always the value that was returned by the previous one. And the chained function has to be a method of the value that was returned. – Barmar Apr 29 '17 at 18:33
  • @guest271314 If you return a proxy, you can't also return a boolean. – Barmar Apr 29 '17 at 18:34
  • @Barmar `context` in respect to `this` within the function. That is different from the value returned from the function, correct? – guest271314 Apr 29 '17 at 18:35
  • A proxy is a function, so you can't chain it (except to use function methods like `.apply`), you have to call it, and it has the desired context. But it still can't also be a Boolean. – Barmar Apr 29 '17 at 18:37
  • @Barmar `$("selector").meetsCondition(condition, function /* this function breaks when condition is met */(bool /* Boolean result of condition parameter */, "selector" as object /* or set different context:this within function, optionally with elements removed from "selector" */) {//do stuff}).hide(/* this:"selector" */)` – guest271314 Apr 29 '17 at 18:38
  • 1
    That's very different, using a callback that receives the boolean. It's essentially a form of Wheeler's Law: Any problem in CS can be solved by adding a level of indirection. – Barmar Apr 29 '17 at 18:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/142979/discussion-between-guest271314-and-barmar). – guest271314 Apr 29 '17 at 18:42
3

jQuery does not have a .every() method. .every is defined at Array.prototype.

You can use .toArray() to convert jQuery object to an Array, within .every() callback function pass current DOM element to jQuery() to get jQuery object representation of element where .hasClass() can be chained.

function submitEnabler(inputs) {
  console.log(inputs.toArray().every(isValid));
}

function isValid(input) {
  return $(input).hasClass('is_glowing_success');
}
guest271314
  • 1
  • 15
  • 104
  • 177
0

I will suggest you use array.map() for example where input is the array input.map(function(input){ return $(input).hasClass('is_glowing_success'); });

this is just an example read more here

Hassan Sani
  • 124
  • 2
  • 9
  • I looked at that...but I'm not really trying to generate another array after processing each element like map does. Thanks. – CodeFinity Apr 29 '17 at 17:49
  • This will return an array of booleans. That won't tell you if every element matches. – Barmar Apr 29 '17 at 18:11