TypeErrors are the second most common error type on the web. They are the JavaScript engine's way of telling the programmer that they're operating on something that has the wrong type. We could be using a number as a function, or using an object as a constructor, or something similar. It turns out that when TypeErrors happen on a live website, almost all of them happen for the same reason - the variable being operated upon has no value: It is null or undefined.
If the variable is declared but has the wrong type, it will produce a TypeError. If it is not declared, it will produce a ReferenceError.
When TypeErrors happen during development, it is often immediately obvious what went wrong. However, when they happen on live websites, they seem to often be a symptom of interdependencies between different parts of the system, where a change in one place cause breakage in another. In our crawl, we found that on live websites, a whole 97% of TypeErrors happen because code wants to operate on a value that is missing. In most cases the code either wants to operate on an element in the document that isn't there, or it wants to use a piece of code that hasn't been loaded. Very often it is because the document has changed, in a way that was unexpected by the code, or because a JavaScript library hasn't loaded.
A common manifestation of the TypeError will have message of the form "Cannot read property 'property_name
' of undefined
/null
". The last word in that message, undefined or null, tells you something about how the error happened:
Below we'll dig into two common scenarios, that illustrate how TypeErrors tend to happen in the wild.
The error indicates that the $
variable is declared, but it is not a function. How does this happen? When loaded, jQuery assigns itself to two global variables $
and jQuery
, which can both be called as a function. However, claiming ownership of the name $
can be considered risky, as it may conflict with any other library using this name. Thus, jQuery provides the method $.noConflict()
which will set the dollar-sign variable back to whatever it was before jQuery was loaded, or set it to undefined if jQuery was the first to touch it. Throwing in the $.noConflict()
line without careful consideration can then ironically lead to conflicts, if later code expects to find jQuery assigned to $
.
For example:
<script src="jquery.js"></script> <script> $.noConflict(); $(document).ready(initSite); </script>
The second use of $
will fail, because the noConflict() call has un-defined the name right underneath our noses. It turns out this situation is extremely common.
This example is specific to jQuery, but it illustrates a common problem: There is an untrue assumption made about the runtime environment, and code will break when it becomes clear that assumption is false. Libraries and plugins that are intended to run in heterogenous environments have no guarantees that its dependencies are met, so they need to check that they are available at runtime, or make dependency problems less likely, for example through the use of package managers.
The most common TypeError are variations of these, where something is done with a value that is null. Most commonly, this comes from missing elements in the document. For example, a programmer could do this:
var el = document.getElementById("my-elem"); el.addEventListener("click", handleClick);
If no element in the document has the id "my-elem", the variable el
will have value null, leading to a crash when we try to call addEventListener.
The twenty most common variants of TypeError messages found in our crawl are the following. Notice a pattern?
TypeError message | Distinct sites with this error |
---|---|
Cannot read property 'envelope' of undefined | 2123 |
$ is not a function | 2108 |
Cannot read property 'addEventListener' of null | 1651 |
Cannot read property 'appendChild' of null | 1378 |
Cannot read property 'msie' of undefined | 1287 |
Cannot read property 'style' of null | 1285 |
Cannot read property 'setRushSimulatedLocalEvents' of undefined | 1109 |
Cannot read property 'top' of undefined | 1063 |
Cannot set property 'innerHTML' of null | 1007 |
Cannot read property 'length' of undefined | 772 |
Cannot set property 'onclick' of null | 640 |
Cannot read property 'split' of undefined | 548 |
Cannot read property 'replace' of undefined | 498 |
e.indexOf is not a function | 494 |
Cannot read property '0' of undefined | 486 |
Cannot read property 'fn' of undefined | 476 |
$(...).live is not a function | 460 |
Cannot read property 'style' of undefined | 436 |
Cannot read property 'querySelector' of null | 428 |
Cannot read property 'add' of null | 381 |
Everything comes from unexpected nulls and undefineds. Note in particular number 1 and 7, between them causing errors on 0.3% of all the crawled websites. These are both related to third-party advertising scripts, whose code content the site owner likely has little control over.