1

I know there are a lot of lightweight JavaScript libraries that do the job, but for my curiosity, I tried this:

function $(a) {

    b = document.querySelectorAll(a);

  [].forEach.call(b, function(d) {

    return d

  });
}

$(".tobered,#illbered").style.border = '1px solid #f00'

And of course my console said: TypeError: $(...) is undefined.

Lets do clearer :

Simplified 'function' :

var $ = document.querySelectorAll.bind(document)

Call which works :

$(".tobered,#illbered")[0].style.border = '1px solid #f00'

Call I need but in one line :

 $(".tobered,#illbered")[0].style.border = '1px solid #f00'
 $(".tobered,#illbered")[1].style.border = '1px solid #f00'
 $(".tobered,#illbered")[2].style.border = '1px solid #f00'
 $(".tobered,#illbered")[...].style.border = '1px solid #f00'

Clearer

How does this works ? https://api.jquery.com/multiple-selector/

Can't figure it out by reading the jquery.js file

Ann MB
  • 146
  • 1
  • 13
  • 6
    There's absolutely no point in doing that initial split on commas, because `.querySelectorAll()` understands that already. The error you're getting is caused by the lack of a `return` value from your `$()` function. – Pointy Apr 07 '18 at 14:15
  • 1
    Possible duplicate of [What does \`return\` keyword mean inside \`forEach\` function?](https://stackoverflow.com/questions/34653612/what-does-return-keyword-mean-inside-foreach-function) – Sebastian Simon Apr 07 '18 at 14:17
  • You're right @Pointy I'm stupid huh. Thx for the duplicate link, I check that – Ann MB Apr 07 '18 at 14:29
  • 3
    I don't think you're stupid :) – Pointy Apr 07 '18 at 14:30
  • @Xufox I don't see the link between this possible duplicate and my question. No matter I use `some()`, `every()`, `for`, `while`, `do`... methods, and I have no `break` issues. However, I might not understand well the usage of `return` in this case. – Ann MB Apr 07 '18 at 14:43
  • @LoargAnn Not using `forEach` would be a step in the right direction. This is part of the problem. The other big problem, already pointed out by Pointy, that there’s no `return` directly in your `$` function — it just selects things, iterates over them, but does nothing with the elements after that. Actually, `map` is the most appropriate Array function here. But the most appropriate thing to do would be to just use `document.querySelectorAll(".tobered, #illbered")`. – Sebastian Simon Apr 07 '18 at 14:45
  • @Xufox Oh ! I have never heard about `map` :) Thx ! About using the whole `document.querySelectorAll` I know that, it's a curiosity question. It's not the first time I'm asking myself how to pass an `Array` of multiple elements and apply them actions. – Ann MB Apr 07 '18 at 14:49
  • 1
    You might want to take a look at my answer to [javascript find() function without jquery](https://stackoverflow.com/questions/33774170/javascript-find-function-without-jquery/33774206#33774206). That's not exactly your question, but might help you. – t.niese Apr 08 '18 at 17:49

2 Answers2

0

There's a conceptual misunderstanding there.

$ is declared but its return value is undefined. It doesn't return anything. Therefore you can't do undefined.style.

But even if it returned something, you would be accessing the style property of whatever it returned. The function has already been executed at that point.

function $(a) {
    b = document.querySelectorAll(a);
    //Calling forEach of an empty array
    //              Each call with b as this
    //                 And the function as parameter
    [].forEach.call(b, function(d) {
        //This actually will never run because there're no elements
        //But if it did, d would be returned to the forEach call
        //and forEach wouldn't do anything with the returned value
        return d
    });
    //The forEach calls returned nothing
    //Function ended and nothing is returned
}
var r=$(...);
//r $(...) is undefined

Note that jQuery uses methods to achieve this, for example

$(a).style(b);

Where $(a) returns an object, and the method of the returned object style() applies the styles b to the selected elements, which are also defined somewhat in the returned object.

So the way to do what you want would be to return some object in $() with properties and methods to apply the styles. Or to have a single function that does everything like $(selector,styles), or two functions, one $() to select (or just use querySelectorAll) and style() that does the forEach thing (like style(elems,styles)).

Gabriel
  • 2,170
  • 1
  • 17
  • 20
  • You're right, jQuery uses methods, I think I tried to use vanilla JS and jQuery syntax which clearly doesn't works. Thx for your answer, I know how to do that right now :) – Ann MB Apr 08 '18 at 19:03
  • Hey Gabriel and @t.niese, I added an answer with my progress, based on your advices, but I still have some issues. Can you help ? :) – Ann MB Apr 09 '18 at 14:14
0

Based on t-niese's comment and Gabriel's answer.

Is that elegant ? Because it's working for me :)

const $ = function(a) {

  let b = [];

  function Reach(els) {
    if (typeof a == "string") {
      b.length = els.length;
      for (var i = 0; i < b.length; i++) {
        b[i] = els[i];
      }
    } else {
      b.push(els);
    }
  }

  Reach.prototype.css = function(prop, val) {
    for (var i = 0; i < b.length; i++) {
      b[i].style[prop] = val;
    }
  }

  Reach.prototype.on = function(evt, fn) {
    for (var i = 0; i < b.length; i++) {
      if (b[i].addEventListener) {
        b[i].addEventListener(evt, fn, false);
      } else if (b[i].attachEvent) {
        b[i].attachEvent('on' + evt, fn);
      } else {
        return false
      }
    }
  }

 if (typeof a == "string") {
   return new Reach(document.querySelectorAll(a))
 } else {
   return new Reach(a)
 }

$(".tobered, #illbered").css("color","red")

$(window).on("click",function(){
  alert('Red is definitely a beautiful color')
})

However I'm doubtful about the fact of using for and (typeof a == "string") ? twice.

Plus, I lost the ability to use $("#id")[0] as a simple selector. If there's no methods, it doesn't works.

I tried so hard to find how to determine if a method is called with Object.prototype.isPrototypeOf() and instanceof to return something else, but I failed.

So right know, I have to use another "function" :

const $$ = document.querySelectorAll.bind(document)

Ann MB
  • 146
  • 1
  • 13
  • 1
    You can have an _object_ with both numeric and string named properties. If `$` returns such object, you could do both `$(..)[0]` (which should be instances of `$` representing each element) and `$(..).css(..)`. The last `typeof a=="string"` is fine, but the first one, inside `Reach()` doesn't make sense. If `els` is a string, the `for` will iterate each character. But `els` will never be other than a list of `Element` because of how you instantiate it. Another thing, remember that `let` is ES6. – Gabriel Apr 10 '18 at 01:16
  • Thx ! It was obvious for the first `typeof` check, I have to stop coding at late hours ^^ But `$(..)[0]` returns `undefined` because $(..) methods returns the object `__proto__` with `Reach`, `css` and `on` :( – Ann MB Apr 10 '18 at 09:48
  • Nope, `$(window)` not being taken without the first `typeof`. See before: https://jsfiddle.net/wf48ukrp/4/ And after: https://jsfiddle.net/wf48ukrp/9/ – Ann MB Apr 10 '18 at 10:06
  • The problem is not the `typeof`, but that you aren't checking if what you got in `els` is an array or a single element. You are trying to pass `window` through the loop. The `$(...)[0]` thing doesn't work because you're storing the elements in `b`... You have to either store the elements in the object (`this`) or implement a `get(i)` method like jQuery (which would be the right thing to do). – Gabriel Apr 11 '18 at 23:29