3

Given the following JavaScript (the relevant HTML will be posted at the bottom of the question):

var app = { // other objects from 'messages' array removed for brevity 'messages': [{ 'author': 'Maya Angelou', 'quote': "If you don't like something, change it. If you can't change it, change your attitude." }], 'textProp': 'textContent' in document.body ? 'textContent' : 'innerText', 'outputTo': document.querySelector('#output'), 'trigger': document.querySelector('#load'), 'quote': function () { var n = Math.floor(Math.random() * this.messages.length), f = document.createElement('figure'), c = document.createElement('figcaption'), frag = document.createDocumentFragment(); f[this.textProp] = this.messages[n].quote; c[this.textProp] = this.messages[n].author; frag.appendChild(f); frag.appendChild(c); this.outputTo.innerHTML = ''; this.outputTo.appendChild(frag); } }; 

We can call the quote() function from outside of the object using the following:

document.getElementById('load').addEventListener('click', app.quote.bind(app)); 

JS Fiddle demo.

Or by simply calling the function directly (not bound as a callback to an event-handler):

app.quote(); 

JS Fiddle demo.

However, I tried to create an event-handler within the object itself, using:

'clickhandler': function(){ this.trigger.addEventListener('click', this.quote); } 

JS Fiddle demo.

This, of course, failed (as expected, since this here is (using an IIFE) this Window object).

I realise that this will, while the object is being created/prior to its initialisation, refer to the Window object, but is there a way I'm not seeing to create, and trigger, the event-handling within the object itself?

I realise that a large portion of my imaginary internet points comes specifically from JavaScript, but learning it accidentally leads to moments of utter confusion and inadequacy; this is not to excuse my ignorance but to explain it.

Finally, the HTML (such as it is):

<button id="load">Switch message</button> <div id="output"></div> 

Incidentally, I've looked at the following linked/suggested questions:

For clarity, I'm trying to create the object itself and have the event-handling created and assigned entirely within/'by' the object, without having to call its methods afterwards. That's the part where I'm stuck (and which I suspect may be impossible).

5
  • 2
    this.trigger.addEventListener('click', this.quote.bind(this)); Commented Jun 5, 2014 at 2:29
  • 1
    What this refers to depends on how you call clickhandler. If you call it "normally", like app.clickhandler(), this refers to app and you should just use .bind as suggested by epascarello . See How to access the correct this / context inside a callback?. Your problem has nothing to do with using object literals. Commented Jun 5, 2014 at 2:30
  • jsfiddle.net/3yj7v/5 Commented Jun 5, 2014 at 2:30
  • @epascarello: what I was trying to do (my question may not have been sufficiently clear, for which I apologise) is to bind the event-handler within the object itself, in order to not have to call the function. Essentially to create just the object and have the handling assigned without making any calls to the object or its methods. Commented Jun 5, 2014 at 2:33
  • 1
    No, that doesn't work. An object literal consists only of key: value pairs, where value can be any expression. But those expressions don't have access to the object itself since it does not exist yet. So in short: You can't reference the object itself during initialization. Commented Jun 5, 2014 at 2:52

5 Answers 5

1

As you specified if you just want to create a new object you probably need to go this way. I think what ever you do you still need to execute something - be it instantiate an object or run a specific init function that binds the click.

 var App = function App(){ this.clickhandler() } App.prototype = { 'messages': [{ 'author': 'Maya Angelou', 'quote': "If you don't like something, change it. If you can't change it, change your attitude." }, { 'author': 'Richard Feynman', 'quote': "Hell, if I could explain it to the average person, it wouldn't have been worth the Nobel prize." }, { 'author': 'Eddie Izzard', 'quote': "Cats have a scam going – you buy the food, they eat the food, they fuck off; that's the deal." }, { 'author': 'George Carlin', 'quote': "I would never want to be a member of a group whose symbol was a man nailed to two pieces of wood. Especially if it's me!" }], 'textProp': 'textContent' in document.body ? 'textContent' : 'innerText', 'outputTo': document.querySelector('#output'), 'trigger': document.querySelector('#load'), 'quote': function () { console.log('hey') var n = Math.floor(Math.random() * this.messages.length), f = document.createElement('figure'), c = document.createElement('figcaption'), frag = document.createDocumentFragment(); f[this.textProp] = this.messages[n].quote; c[this.textProp] = this.messages[n].author; frag.appendChild(f); frag.appendChild(c); this.outputTo.innerHTML = ''; this.outputTo.appendChild(frag); }, 'clickhandler' : function(){ this.trigger.addEventListener('click', this.quote.bind(this)); } }; //just create an object app = new App(); 

http://jsfiddle.net/LwrvT/

Sign up to request clarification or add additional context in comments.

Comments

1

At some point, you will need to .bind() the method to your app (unless you avoid the use of this and replace it with app everywhere). This is however not necessarily in the place where you pass the app.quote method (e.g. bind as the event listener), but might be directly after the declaration of the app object:

var app = { …, quote: function() { … this … } }; app.quote = app.quote.bind(app); 

If you have Underscore around, you might use the bindAll helper function for this:

var app = _.bindAll({ …, quote: function() { … this … } }, "quote"); 

If you are not in an object literal - it could be a constructor, IEFE, whatever - you can .bind() the function directly at the place of its declaration:

function App() { … this.quote = function() { … this … }.bind(this); } 

With coffeescript or ES6, you can also use the fat-arrow function syntax as a sugar for this.

Comments

1

Instead of object literal, you could do the below:

var app = new function () { this.messages = [{ 'author': 'Maya Angelou', 'quote': "If you don't like something, change it. If you can't change it, change your attitude." }, { 'author': 'Richard Feynman', 'quote': "Hell, if I could explain it to the average person, it wouldn't have been worth the Nobel prize." }, { 'author': 'Eddie Izzard', 'quote': "Cats have a scam going – you buy the food, they eat the food, they fuck off; that's the deal." }, { 'author': 'George Carlin', 'quote': "I would never want to be a member of a group whose symbol was a man nailed to two pieces of wood. Especially if it's me!" }]; this.textProp = 'textContent' in document.body ? 'textContent' : 'innerText'; this.outputTo = document.querySelector('#output'); this.trigger = document.querySelector('#load'); this.quote = function () { var n = Math.floor(Math.random() * this.messages.length), f = document.createElement('figure'), c = document.createElement('figcaption'), frag = document.createDocumentFragment(); f[this.textProp] = this.messages[n].quote; c[this.textProp] = this.messages[n].author; frag.appendChild(f); frag.appendChild(c); this.outputTo.innerHTML = ''; this.outputTo.appendChild(frag); }; this.trigger.addEventListener('click', this.quote.bind(this)); }; 

SEE THE WORKING DEMO.

4 Comments

Is there a reason why you have one long expression using the comma operator, instead of using multiple expression statements?
@FelixKling No, it should be ;, I just copied the code from the op's fiddle and forgot to change it :)
@Bergi Although the constructor could be leaked this way, but this is under your control, right? Unless you use app.constructor, there should be no problems.
I'm not so much concerned with "loosing control", but more with memory consumption. There exist two completely superflouos objects which could so easily be avoided. E.g, omit the new and let the function end in return this;}.call({}).
0

The this variable is just referencing app. So just use app.

var app = { someVar: 'thing', someMethod: function(){ alert(app.someVar); } }; 

or you can do

function createApp(){ var app = {}; app.someVar = 'thing'; app.someMethod = function(){ alert(app.someVar); }; return app; } 

Comments

0

A small change. Declaring the Object before initializing its properties might help your usecase.

var app = {}; app["messages"] = "test message"; app["textProp'] = 'textContent' in document.body ? 'textContent' : 'innerText'; app['quote']= function () { var n = Math.floor(Math.random() * this.messages.length), f = document.createElement('figure'), c = document.createElement('figcaption'), frag = document.createDocumentFragment(); f[app.textProp] = app.messages[n].quote; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.