28

I'm building a node scraper that uses cheerio to parse the DOM. This is more or a vanilla javascript question though. At one part of my scrape, I'm loading some content into a variable, then checking the variable's length, like so:

var theHref = $(obj.mainImg_select).attr('href');
if (theHref.length){
   // do stuff
} else {
  // do other stuff
}

This works just fine, until I came across a url for which $(obj.mainImg_select).attr('href') didn't exist. I assumed that my theHref.length check would account for this and skip through to the else: do other stuff statement, but instead I got:

TypeError: Cannot read property 'length' of undefined

What am I doing wrong here and how can I fix this?

Martijn Vissers
  • 712
  • 5
  • 29
JVG
  • 20,198
  • 47
  • 132
  • 210
  • 3
    `theHref` is undefined and thus, you can not read any property of it - for example `length` . In case that wasn't clear - this is a bug in cheerio which says that its API is supposed to be identical to jQuery's in attr. – Benjamin Gruenbaum Jul 11 '13 at 00:58
  • Update, this isn't a bug but was changed in cheerio, which now abides to the newer jQuery API which does return undefined. – Benjamin Gruenbaum Jul 11 '13 at 01:04
  • make sure that you properly attached the event by selecting id or class name etc. – Usman Younas May 04 '15 at 10:43

7 Answers7

54

You can check that theHref is defined by checking against undefined.

if (undefined !== theHref && theHref.length) {
    // `theHref` is not undefined and has truthy property _length_
    // do stuff
} else {
    // do other stuff
}

If you want to also protect yourself against falsey values like null then check theHref is truthy, which is a little shorter

if (theHref && theHref.length) {
    // `theHref` is truthy and has truthy property _length_
}
Paul S.
  • 64,864
  • 9
  • 122
  • 138
6

Why?

You asked why it happens, let's see:

The official language specificaion dictates a call to the internal [[GetValue]] method. Your .attr returns undefined and you're trying to access its length.

If Type(V) is not Reference, return V.

This is true, since undefined is not a reference (alongside null, number, string and boolean)

Let base be the result of calling GetBase(V).

This gets the undefined part of myVar.length .

If IsUnresolvableReference(V), throw a ReferenceError exception.

This is not true, since it is resolvable and it resolves to undefined.

If IsPropertyReference(V), then

This happens since it's a property reference with the . syntax.

Now it tries to convert undefined to a function which results in a TypeError.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
4

There's a difference between an empty string "" and an undefined variable. You should be checking whether or not theHref contains a defined string, rather than its lenght:

if(theHref){
   // ---
}

If you still want to check for the length, then do this:

if(theHref && theHref.length){
   // ...
}
JCOC611
  • 19,111
  • 14
  • 69
  • 90
4

In addition to others' proposals, there is another option to handle that issue.

If your application should behave the same in case of lack of "href" attribute, as in case of it being empty, just replace this:

var theHref = $(obj.mainImg_select).attr('href');

with this:

var theHref = $(obj.mainImg_select).attr('href') || '';

which will treat empty string ('') as the default, if the attribute has not been found.

But it really depends, on how you want to handle undefined "href" attribute. This answer assumes you will want to handle it as if it was empty string.

Tadeck
  • 132,510
  • 28
  • 152
  • 198
1

If you aren't doing some kind of numeric comparison of the length property, it's better not to use it in the if statement, just do:

if(theHref){
   // do stuff
}else{
  // do other stuff
}

An empty (or undefined, as it is in this case) string will evaluate to false (just like a length of zero would.)

AlliterativeAlice
  • 11,841
  • 9
  • 52
  • 69
  • Empty and undefined mean two fundamentally different things. For example, a cheerio selector like in OP's case (or a jQuery one on the web) can have length 0 and still not evaluate to false. – Benjamin Gruenbaum Jul 11 '13 at 01:01
  • OP is evaluating a string, however. As of jQuery 1.6, all .attr() calls always return either a string or undefined. – AlliterativeAlice Jul 11 '13 at 01:03
1

As has been discussed elsewhere, the .length property reference is failing because theHref is undefined. However, be aware of any solution which involves comparing theHref to undefined, which is not a keyword in JavaScript and can be redefined.

For a full discussion of checking for undefined variables, see Detecting an undefined object property and the first answer in particular.

Community
  • 1
  • 1
Peter Alfvin
  • 28,599
  • 8
  • 68
  • 106
0

You can simply check whether the element length is undefined or not just by using

var theHref = $(obj.mainImg_select).attr('href');
if (theHref){
   //get the length here if the element is not undefined
   elementLength = theHref.length
   // do stuff
} else {
   // do other stuff
}
Sundeep Pidugu
  • 2,377
  • 2
  • 21
  • 43