1

I have this simple function that copies some html, and places it in another div. If I put the code for the function in the click event it works fine, but when I move it into a function (to be used in multiple places) it no longer works. Do you know why this is?

If I console.log($(this)); in the function it returns the window element.

 function addHTMLtoComponent () {
        var wrapper = $(this).closest(".wrapper");
        var component = $(wrapper).find(".component");
        var componentCodeHolder = $(wrapper).find('.target');   

         $(componentCodeHolder).text(component.html())
      //console.log($(this));
 }

 $(".js_show_html").click(function () {
     addHTMLtoComponent();
 });

codepen here - http://codepen.io/ashconnolly/pen/ebe7a5a45f2c5bbe58734411b03e180e

Should i be referencing $(this) in a different way?

Ash
  • 202
  • 3
  • 16
  • 2
    You need to pass `$(this)` to your function: `function addHTMLtoComponent($this) {}`, and call it with `addHTMLtoComponent($(this))` – TAGraves May 25 '16 at 14:18
  • 1
    the function does not know who the caller is so it does not know who `this` refers to, you need to pass it (as a parameter) yourself – blurfus May 25 '16 at 14:19
  • 1
    Also, have a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this for more info on why `this` works the way it does. – TAGraves May 25 '16 at 14:19
  • Possible duplicate of [How does the "this" keyword work?](http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – James Thorpe May 25 '16 at 14:19
  • 3
    `$(".js_show_html").click(addHTMLtoComponent);` – A. Wolff May 25 '16 at 14:20
  • The problem here isn't really related to jQuery - it's the way `this` works in JavaScript. The linked duplicate goes into excruciating detail of how it works. – James Thorpe May 25 '16 at 14:20

6 Answers6

6

Regarding other answers, i need to put the easiest one:

$(".js_show_html").click(addHTMLtoComponent);
A. Wolff
  • 72,298
  • 9
  • 84
  • 139
  • Interesting! Does this work because there is not wrapper function absorbing `this`? – cyberbit May 25 '16 at 14:26
  • 2
    @cyberbit jQuery arranges for `this` to refer to the element involved in the event. – Pointy May 25 '16 at 14:29
  • 2
    @Pointy Right, I was just pointing out that the OP's solution had `addHTMLtoComponent` wrapped in another function, so the context got overwritten. Upvoted for simplest solution. – cyberbit May 25 '16 at 14:33
  • This is definitely the right solution, but OP should still learn about how `this` works – mdickin May 25 '16 at 14:37
  • Thanks mate! I listed the other answer as correct as I used that solution - because I wanted to call further functions within the click function. But your way of doing it is helpful and easy! Thanks! – Ash Jun 08 '16 at 11:03
  • Apparently in this case there is something provided as a parameter. It seems to be the HTML tag which triggered the event. I didn't find any documentation regarding it - can you help me and provide any link to some documentation regarding this? – Peter VARGA Nov 23 '20 at 16:25
3

this in the context of the click() event is the element clicked. In the context of the function addHTMLtoComponent this no longer is a reference to the element clicked.

Try passing the clicked object to the function to maintain the object reference.


function addHTMLtoComponent ($obj) {
    var $wrapper = $obj.closest(".wrapper");
    var $component = $wrapper.find(".component");
    var $componentCodeHolder = $wrapper.find('.target');
    $componentCodeHolder.text($component.html());
}

$(".js_show_html").click(function () {
    addHTMLtoComponent($(this));
});
hunter
  • 58,834
  • 17
  • 108
  • 112
  • I know this answers the question, but shouldn't `addHTMLtoComponent` be called with the value of `this`? e.g. `addHTMLtoComponent.call(this);` rather than blindly passing jQuery objects around? – David Barker May 25 '16 at 14:23
  • @DavidBarker If the function is written to always accept a jQuery object, and is documented as such, why not pass it one? – James Thorpe May 25 '16 at 14:31
  • 1
    I have to agree with David. The advantage of using `this` instead of `$(this)` is that it supports everything the jQuery constructor supports, including both elements *and* jQuery objects (http://api.jquery.com/jQuery/). – cyberbit May 25 '16 at 14:34
  • 1
    @DavidBarker it's not blindly passing anything, more so it is specifically passing a jQuery object. using `this` or `$(this)` doesn't really matter or solve the problem but explaining what `this` is, contextually, is what the OP is looking for, I think. – hunter May 25 '16 at 15:30
3

since you called the function manually it doesn't know the "this" context, therefore it revert back to use the window object.

$(".js_show_html").click(function () {
     addHTMLtoComponent();
 });

// Change to this

$(".js_show_html").click(function () {
     addHTMLtoComponent.call(this);
 });

Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

Du D.
  • 3,819
  • 1
  • 24
  • 31
0

You have to pass the element as parameter to this function. eg:

<div onclick="addHTMLtoComponent ($(this))"></div>
James Thorpe
  • 28,613
  • 5
  • 64
  • 82
Ansar
  • 376
  • 1
  • 12
0

One thing you could consider is that addHTMLtoComponent() could be made into a jQuery function itself:

$.fn.addHTMLtoComponent = function() {
    return this.each(function() {
      var wrapper = $(this).closest(".wrapper");
      var component = $(wrapper).find(".component");
      var componentCodeHolder = $(wrapper).find('.target');   

      componentCodeHolder.text(component.html())
    });
}

Now you can call it like any other jQuery method:

$(".js_show_html").click(function () {
   $(this).addHTMLtoComponent();
});

The value of this in a jQuery method will be the jQuery object itself, so you don't need to re-wrap it with $(). By convention (and when it makes sense), jQuery methods operate on all elements referred to by the root object, and they return that object for further chained operations. That's what the outer return this.each() construction does.

Inside the .each() callback, you've got a typical jQuery callback situation, with this being set successively to each member of the outer jQuery object.

Pointy
  • 371,531
  • 55
  • 528
  • 584
  • Why pollute the fn space just for that? – Du D. May 25 '16 at 14:25
  • 2
    @DuD. why not? It's not sacred or anything, and so long as one is fairly careful with method names collisions are not very likely. If this is important functionality for an application it's not a bad idea. Of course, that's a matter of opinion, and you're correct that it *does* live in the flat jQuery method namespace. – Pointy May 25 '16 at 14:28
0

The special keyword this, when you call a function by itself, is the window object (which is what you observed). For the behavior you need, just add a parameter to the function that loads the appropriate context:

 function addHTMLtoComponent(context) {
   var wrapper = $(context).closest(".wrapper");
   var component = $(wrapper).find(".component");
   var componentCodeHolder = $(wrapper).find('.target');

   $(componentCodeHolder).text(component.html())
     //console.log($(context));
 }

 $(".js_show_html").click(function() {
   addHTMLtoComponent(this);
 });
cyberbit
  • 1,250
  • 15
  • 22