0

At the moment the id method in this object will alert undefined as it is called before the document has loaded. I have a method for checking if the dom has loaded but am stuck on how to get that method to trigger the id method when the dom has loaded.

I can't simply add a call to the id method in the dom loaded method as it will be used for other unrelated tasks.

function myHandler(sel) {
    function myObject(sel) {
        this.sel = sel;
        myObject.prototype.init = function () {
            this.ready();
        }
        myObject.prototype.id = function () {
            alert(document.getElementById(this.sel).id);
        }
        myObject.prototype.ready = function () {
            document.onreadystatechange = function () {
                var state = document.readyState
                if (state == 'interactive') {} else if (state == 'complete') {}
            }
        }
    }
    return newTask = new myObject(sel);
    newTask.init;
}
myHandler('element').id();
janfoeh
  • 10,243
  • 2
  • 31
  • 56
Guesser
  • 1,769
  • 3
  • 25
  • 52
  • `newTask.init;` is never executed because you return right before it. – MrCode Apr 20 '14 at 16:21
  • 1
    There are few issues in your code. a) newTask.init is not a function call, this will do nothing, further more this even won't be executed because it's after return statement... b) you have syntax error, should be `myHandler('element').id();` but assume it's a typo on stackOverflow. if you depend on elements in dom execute your script on domready, for example : `document.addEventListener("DOMContentLoaded", function () { myHandler('element').id(); }, false);` – PolishDeveloper Apr 20 '14 at 16:22
  • Additionally, you modify `myObject`s prototype over and over again, every time the function is called, which does not really make sense. – janfoeh Apr 20 '14 at 16:26
  • so how would I get this to work `myJob.prototype.id=function(){ document.addEventListener("DOMContentLoaded", function (this) { alert(document.getElementById(this.sel).id); }, false); }` as the 'this' reference is out of scope. – Guesser Apr 20 '14 at 17:03

1 Answers1

0

Here is a rewritten version of your code:

var MyObject = function MyObject(selector) {
    this.selector = selector;

    this.init();
};

MyObject.prototype.alertId = function alertId() {
    alert(this.selector);
};

MyObject.prototype.init = function init() {
    var that = this;

    // are we there already?
    if (document.readyState == "interactive" ||
        document.readyState == "complete") {

        // yes, fire immediately
        this.alertId();
    } else {

        // no, wait until the DOM is parsed
        document.addEventListener("DOMContentLoaded", function(event) {
            that.alertId();
        });
    }
};

var my_object = new MyObject('foo');

Let's go through this:

var MyObject = function MyObject(selector) {
    this.selector = selector;

    this.init();
};

This is the constructor for MyObject objects. As a convention, constructor functions are generally capitalized in Javascript, so I have renamed it.

In the constructor, we set up all the properties that are unique to each copy of MyObject you create by calling new MyObject('selector string'). In this case, it's just the selectorwe get as the argument. After that, we call init() on it on our new object.

Every MyObject you create does not need its own init() and alertId() - it behaves the same for every copy. So in order not to waste memory, we create it on the prototype which all MyObjects share:

MyObject.prototype.alertId = function alertId() {
    alert(this.selector);
};

We could be certain that our selector is already available if you would include this script as the very last element at the bottom of your page, right before the closing tag. But a more robust way is to check:

MyObject.prototype.init = function init() {
    var that = this;

    // are we there already?
    if (document.readyState == "interactive" ||
        document.readyState == "complete") {

        // yes, fire immediately
        this.alertId();
    } else {

        // no, wait until the DOM is parsed
        document.addEventListener("DOMContentLoaded", function(event) {
            that.alertId();
        });
    }
};

If our DOM is already available to us, we execute alertId() immediately. If not, we wait until it is by registering a callback for the DOMContentLoaded event.

Note that I used that.alertId() in the callback, not this.alertId(). This is because when our callback function is executed, this will be the window context, not our current MyObject object.

To get around this, I saved our current this pointing to our object in the variable that. that remains available inside our callback function because Javascript has closures.

Please note that the above code only works for modern browsers. If you need compatibility with Internet Explorer 8 and older, you will need to workaround its missing support for DOMContentLoaded with something like ContentLoaded.

Community
  • 1
  • 1
janfoeh
  • 10,243
  • 2
  • 31
  • 56
  • Usually one would free MyObject from the responsibility of knowing whether the DOM is ready or not. Instead, shift the responsibility to the place where you create and call MyObject. In short: put `new MyObject(..); my_object.alertAgain()` in a DOMContentLoaded callback. – janfoeh Apr 20 '14 at 18:29
  • That's all good but it's not quite what I'm getting at, I can add a method to your object `MyObject.prototype.alertAgain = function () { alert(document.getElementById(this.selector).id); };` but this will not work as the the new method is called before the document is ready `var my_object = new MyObject('some_id'); my_object.alertAgain();` I need to ensure calling a method on an instantiated object can only occur once the DOM has loaded, or rather it should wait until the DOM has loaded before trying. – Guesser Apr 20 '14 at 18:31