2

I am creating a plugin using jQuery library.

Here i am storing String.prototype in a variable then i am using this variable to extend my Sting object. And this is working fine.

// String Prototyping store in a variable
// Save bytes in the minified version of js
var StrProto = String.prototype;
String.prototype.toProperCase = function () {
  return this.replace(/\w\S*/g, function (txt) {
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });
};
// working fine
alert("yogesh kumar".toProperCase());

In the next case i am creating m function xyz which stored in abc variable and this is also working fine.

function xyz(x){
  alert(x)
}
var abc = xyz;
// working fine
abc("yogesh kumar");

In the last case i am storing document.createElement in a variable tag and using tag to create a button. but this is not working.

var tag=document.createElement;
$(document.createElement("button")).html("document.Element").appendTo("#myDiv");

// not working
$(tag("button")).html("tag").appendTo("#myDiv");

Please check the link on jsFiddle:

click here

Error:

In Chrome

  • Uncaught TypeError: Illegal invocation

in Firefox

  • Error: NS_ERROR_XPC_BAD_CONVERT_JS: Could not convert JavaScript argument

Why this error?

What is the solution?

Mike Fielden
  • 10,055
  • 14
  • 59
  • 99
yoku2010
  • 600
  • 1
  • 4
  • 14
  • 2
    My guess would be it loses it's context when called that way. For example, `tag.call(document,"button")` works. http://jsfiddle.net/hA2pJ/5/ – Kevin B Jan 07 '13 at 19:31

5 Answers5

8

You are getting a reference to a function that is a member of the document. When you call that reference directly, it's context is now the window rather than the document. Here's an example:

http://jsfiddle.net/DeCNx/

var foo = {
  createElement: function(tagname) {
    if (this._secretvarthatisneeded) {
      console.log(tagname + " Element Created!");
    }
  },
  _secretvarthatisneeded: true
}

foo.createElement("FOOBAR"); // works

var bar = foo.createElement;
bar("BARFOO"); // doesn't work
bar.call(foo,"BARBAR") // works

Since the context was lost, the bar() call didn't result in a console.log();

obviously this is just a simplification to demonstrate.

Update: For the use you are making, i'd suggest doing this:

$.createElement = function(tagName,attributes){
    return $(
        document.createElement(tagName),
        attributes ? attributes : {}
    )
}

Now you can simply do $.createElement("button").html("tag").appendTo("#myDiv"); It is fast and still easy to read. Note however IE has problems with inputs, if you're creating input elements, i suggest using $("<input type='text' />") rather than this.

Kevin B
  • 94,570
  • 16
  • 163
  • 180
5

Use the bind() method for "assigning" the native JS method to a variable:

var ce = document.createElement.bind(document);
var elem = ce('div');
alert(elem.nodeName);

Works in modern browsers including IE9+. For older browsers, use a wrapper function.

Marat Tanalin
  • 13,927
  • 1
  • 36
  • 52
4

jQuery can create new elements for you as simple as:

$("<button />").html("document.Element").appendTo("#myDiv");

To have a reason why your approach is not working, read @Kevin's comment below.

Community
  • 1
  • 1
VisioN
  • 143,310
  • 32
  • 282
  • 281
  • my Question is why i can't store document.createElement in a javascript veriable – yoku2010 Jan 07 '13 at 19:32
  • 3
    @yoku2010 You can, however, since it is no longer being called as a member of `document`, it's `this` (or context) is no longer the document. See this fiddle: http://jsfiddle.net/hA2pJ/5/ David's answer would be a more proper way of wrapping the `document.createElement` method. – Kevin B Jan 07 '13 at 19:33
2

That is happening because document.createElement uses this inside itself. When you call it like document.createElement() then this is set to document. But, when you save it as a variable, then this is no longer document, it's window.

You need to call it with the context.

var tag = document.createElement;  // you are saving the function, not its context
var btn = tag.call(document, 'button'); // you need to set the context

If your browser supports it, you can also use .bind:

var tag = document.createElement.bind(document);
var btn = tag('button');
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
2

The reason for this error is that the method lost its context. The method createElement() must be called in the context of a document object.

Try this in a console:

var tag = document.createElement;
tag.call(document, "div");  // no error
tag("div");  // error

The specific details of why createElement() must be called in the context of document are implementation specific, but can easily be guessed at.

So, to maintain context, create a function wrapper for document.createElement():

function tag(tagName) {
    return document.createElement(tagName);
}

Of course, jQuery will also create elements for you:

$("<div>");  // new div element
gilly3
  • 87,962
  • 25
  • 144
  • 176
  • 1
    $(document.createElement('div')) is faster then $('
    ') and $('
    '). please check this: http://jsperf.com/document-createelement-vs-jquery-html-string
    – yoku2010 Jan 07 '13 at 19:55