2

I'm having a problem with Underscore.js templates and Internet Explorer. Here's part of the template which is causing trouble:

<p>
  <% if ( typeof description !== 'undefined' ) { %>
    <%- description %>
  <% } else { %>
    No description
  <% } %>
</p>

When the variable description is undefined (which means I'm not supplying it to the template at all, the variable does not exist), this works just fine in Safari, Firefox, Chrome.

Internet Explorer however, doesn't work correctly. Instead of showing No description IE8 and IE9 show [object HTMLMetaElement], and IE7 shows [object].

Checking the result of typeof description returns undefined in Safari, Firefox, Chrome, but apparently Internet Explorer returns object instead.

I already tried Underscore.js's _.isUndefined(value) function, but that one doesn't work when the variable does not exist.

Does anyone know a workaround for this problem? (note that I am unable to supply the variable with no value - it either exists, or it doesn't)

Update I found a workaround in one of the Underscore.js Github issues https://github.com/documentcloud/underscore/issues/237#issuecomment-1781951

Can someone explain why IE behaves differently, and why the workaround actually works?

Update 2 @John-DavidDalton has provided another, better workaround in the comments below (linking directly to it doesn't seem to work)

2 Answers 2

5

From the fine manual:

By default, template places the values from your data in the local scope via the with statement.

You can see what's going on using the compiled template's source property:

​var t = _.template('<%= v %>');
console.log(t.source);​​​​​​​​​​​​​​​​​​​

gives you (tweaked for clarity):

function(obj) {
    var __t, __p = '';
    with(obj || {}) {
      __p += '' + ((__t = ( v )) == null ? '' : __t) + '';
    }
    return __p;
}

The with is the source of your problem:

JavaScript looks up an unqualified name by searching a scope chain associated with the execution context of the script or function containing that unqualified name. The 'with' statement adds the given object to the head of this scope chain during the evaluation of its statement body. If an unqualified name used in the body matches a property in the scope chain, then the name is bound to the property and the object containing the property. Otherwise a 'ReferenceError' is thrown.

So given this:

with(obj) {
    console.log(pancakes)
}

JavaScript will first look for obj.pancakes and if there is no pancakes property in obj, it will look for pancakes in the global scope. Apparently IE has a window.description value which represents one of the page's <meta> tags. Using your own namespace inside the template (as in the work-around) sort of negates what with does and keeps you from getting to the global window properties.

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

5 Comments

You can avoid the default with-statement by passing a variable option: _.template('<%= obj.v %>', null, { 'variable': 'obj' }); See underscorejs.org/#template
@John-DavidDalton: I think that's an even better solution than the one I found in the Github issue.
The variable trick might not have been available a year ago when that Github issue was being discussed. But yeah, variable is a useful trick.
@John-DavidDalton: You can write that up as an answer and René can change his mind about which one he wants to accept if you'd like, I promise I won't throw a hissy fit.
@muistooshort actually your answer explains perfectly what was wrong and why the workaround works, so no need to add another answer imo.
0

If you typeof something and the something is null, object is returned. Try checking for null after checking for 'undefined'.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.