5

I don't know where to start but what I want or need is a wrapper function for HTMLElement so I don't extend HTMLElement class but etend my own object and then check has class to see if the element has a class etc etc but my code doesn't work at all it says $(...).hasClass is not a function

$ = function(a, b) {
  this.$el = document.querySelector(a);
  return this;
}

Object.assign($.prototype, {
  hasClass: function(selector) {
    return this.$el.classList.contains(selector);
  }
})

console.log($('#list').hasClass('list'));
ONYX
  • 5,679
  • 15
  • 83
  • 146

2 Answers2

3

You're almost there - the issue is that the standalone expression

$('#list')

will have the default calling context of window (the this value inside the $ function). If you want to create an instance which can use prototype methods, put new before the call to $:

const $ = function(a, b) {
  this.$el = document.querySelector(a);
}

Object.assign($.prototype, {
  hasClass: function(selector) {
    return this.$el.classList.contains(selector);
  }
})

console.log(new $('#list').hasClass('list'));
console.log(new $('#list2').hasClass('foo'));
<div id="list"></div>
<div id="list2" class="foo"></div>

If you don't want to put new before every call, you can use Object.create inside $:

const $ = function(a, b) {
  const $obj = Object.create(proto);
  $obj.$el = document.querySelector(a);
  return $obj;
}

const proto = {
  hasClass: function(selector) {
    return this.$el.classList.contains(selector);
  }
};

console.log($('#list').hasClass('list'));
console.log($('#list2').hasClass('foo'));
<div id="list"></div>
<div id="list2" class="foo"></div>

I think the way jQuery does it is, the returned object's internal prototype is $.fn, eg:

const $ = function(a, b) {
  const $obj = Object.create($.fn);
  $obj.$el = document.querySelector(a);
  return $obj;
};
$.fn = {
  hasClass: function(selector) {
    return this.$el.classList.contains(selector);
  }
};

console.log($('#list').hasClass('list'));
console.log($('#list2').hasClass('foo'));
<div id="list"></div>
<div id="list2" class="foo"></div>
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Ok cool but I don't want to hacve to pu the keyword "new" infront of $('') everytime so whats the work around for that – ONYX Apr 15 '19 at 03:31
  • Use `Object.create`, as in the second snippet – CertainPerformance Apr 15 '19 at 03:32
  • it says proto is undefined – ONYX Apr 15 '19 at 03:33
  • Press the "Run code snippet" button in the answer to see it in action - it works just fine here. Are you sure you defined `proto` in your code? – CertainPerformance Apr 15 '19 at 03:34
  • One of my points was to use Object.assign($.prototype, {}); and now you've taken it away – ONYX Apr 15 '19 at 03:39
  • You don't have to `Object.assign` to anything with this method, you can simply define the prototype object outright - there's nothing wrong with that, in fact, it's probably preferable. Mutation is best avoided when not necessary, in most cases. – CertainPerformance Apr 15 '19 at 03:40
  • Thank you for your help today, you have been teaching me new things – ONYX Apr 15 '19 at 03:42
  • The [other question](https://stackoverflow.com/questions/55681764/how-to-set-up-multiple-function-in-an-object-prototype/55681787#55681787) *required* `Object.assign` (or something like it that involved mutation) because you *had* to mutate an existing object, `HTMLElement.prototype`, but here, since you're creating the prototype yourself, you can just declare that prototype. – CertainPerformance Apr 15 '19 at 03:43
  • I used backbone a while back and when rendering the node backbone had the property "el" so you would call myelement.render().el and there was also the jquery "$el" in the chain I'm confused to what's what, do you know which is which so I can store that information correctly – ONYX Apr 15 '19 at 03:48
  • The returned object's `$el` property will reference the selected element (if any), eg `$('#list').$el` will work in any of the snippets, if that's what you're looking for. As you can see by running them, there's no need to do that if you're just trying to call one of the prototype methods – CertainPerformance Apr 15 '19 at 03:50
1

Alternate Method do with return function inside the function

const $ = function(a, b) {
  var elem = document.querySelector(a);
  return {
    hasClass: function(selector) {
      return elem.classList.contains(selector);
    },
    addClass:function(cls){
      elem.classList.add(cls)
    }
  }
};

console.log($('#list').hasClass('list'));
console.log($('#list2').hasClass('foo'));
$('#list2').addClass('color_red');
.color_red{
  color:red
}
<div id="list"></div>
<div id="list2" class="foo">hello</div>
prasanth
  • 22,145
  • 4
  • 29
  • 53