1

I have planned to get a rid of jQuery, just to learn more plain javascript to build my own libraries. I have created function __ similar to jQuery's $. First prototype method I choose to be _addClass which actually add a class to DOM element(s);

Html code is simple:

(() => {
    function __(arg) {
        if (!(this instanceof __))
            return new __(arg);
        this.element = null;
        this.elements = null;
        this.singleElement = false;
        switch (arg.substring(0, 1)) {
            case ".":
                this.elements = document.getElementsByClassName(arg.substring(1));
                break;
            case "#":
                this.element = document.getElementById(arg.substring(1));
                this.singleElement = true;
                break;
            case "&":
                this.elements = document.getElementsByName(arg.substring(1));
                break;
            default:
                this.elements = document.querySelectorAll(arg);
                break;
        }
        return this;
    }

    __.prototype._addClass = (a) => {
        if (this.singleElement) {
            this.element.classList.add(a);
            return this.element;
        } else {
            this.elements.forEach((element) => element.classList.add(a));
            return this.elements;
        }
    }

    document.getElementById("btn").addEventListener("click", () => {
        __("#test")._addClass("new-class-style");
    })
})();
<div id="test">Div content</div>
<button id="btn">Button</button>

However, I'm getting error:

Uncaught TypeError: Cannot read properties of undefined (reading 'classList')

pointing to this.element or this.elements or this.singleElement are undefined and I just can't figure why?

Edit:

Concept was actually OK, but I often use arrow function instead of regular javascript function since in classes in callback with function, this is not working, so I use arrow function in which this is working in callback. And I just haven't realized that in declaring prototype I cannot use arrow functions. And calling window.__ = __; is needed to use out of this scope.

Final working version, with all suggested changes will be:

(() => {
    function __(selector) {
        if (!(this instanceof __))
        return new __(selector);
        this.elements = null;
        if (selector !== undefined) 
            this.elements = document.querySelectorAll(selector);
        return this;
    }

    __.prototype.addClass = function (...a) {
        this.elements.forEach((element) => {
            a.forEach((className) => element.classList.add(className));
        });
        return this;
    }
    __.prototype.removeClass = function (...a) {
        this.elements.forEach((element) => {
            a.forEach((className) => element.    classList.remove(className));
    });
        return this;
    }
    __.prototype.html = function (a) {
        if (a === undefined) {
            let out = "";
            this.elements.forEach((element) => out = element.innerHTML);
            return out;
        }
        this.elements.forEach((element) => element.innerHTML = a);
        return this;
    }
    __.prototype.text = function (a) {
        if (a === undefined) {
            let out = "";
            this.elements.forEach((element) => out = element.innerText);
            return out;
        }
        this.elements.forEach((element) => element.innerText = a);
        return this;
    }
    __.prototype.on = function (type, callback) {
        this.elements.forEach((element) => {
            element.addEventListener(type, callback);
        });
        return this;
    }
    window.__ = __;
})();

__("#btn").on("click", () => {
    __("#test").addClass("some-class").text("Div content chaged");
})
<div id="test">Div content</div>
<button id="btn">Button</button>

1 Answers1

1

That's because you're using an arrow function for your _addClass method and so the this keyword becomes undefined. Changing it back to normal function fixes the issue:

 (() => {
        function __(arg) {
          if (!(this instanceof __)) return new __(arg);
          this.element = null;
          this.elements = null;
          this.singleElement = false;
          switch (arg.substring(0, 1)) {
            case '.':
              this.elements = document.getElementsByClassName(arg.substring(1));
              break;
            case '#':
              this.element = document.getElementById(arg.substring(1));
              this.singleElement = true;
              break;
            case '&':
              this.elements = document.getElementsByName(arg.substring(1));
              break;
            default:
              this.elements = document.querySelectorAll(arg);
              break;
          }
          return this;
        }

        __.prototype._addClass = function (a) {
          if (this.singleElement) {
            this.element.classList.add(a);
            return this.element;
          } else {
            this.elements.forEach((element) => element.classList.add(a));
            return this.elements;
          }
        };

        document.getElementById('btn').addEventListener('click', () => {
          __('#test')._addClass('new-class-style');
        });
      })();
    <div id="test">Div content</div>
    <button id="btn">Button</button>
Joshua
  • 3,055
  • 3
  • 22
  • 37