jquery selectors with option (select) throwing error - javascript

I noticed this after a user reported the behavior since it was working fine on Chrome, Firefox and IE8+. However in IE7 the following selectors:
$('#parentjobs option')
$('#parentjobs option:not(:contains("new"))')
throw this error:
Error: Unable to get value of the property '0': object is null or undefined
Which points to line 5126 on jquery-1.8.3:
context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
I don't have IE7 installed but I can reproduce the error when I switch the document mode and browser mode to IE7 on the IE debug page (F12)
Any ideas what might be going wrong?

I'm not sure what the problem is, but something that might make it work cross-browser, in a different way, is to use filter:
$("#parentjobs").find("option").filter(function () {
return $(this).text().indexOf("new") === -1;
});
Which says "only include the <option> elements that don't contain the text 'new'"...and should do the same as your original selector.
filter includes/excludes elements based on the result of the return. If true is returned, the element is left in the list. If false is returned, the element is removed from the list.
Using .text() is the same as what :contains() looks at, and === -1 means the text isn't found.
It might just be me, but I like to be more explicit with selecting elements, staying away from long selectors (especially pseudo selectors) and using jQuery methods instead. Although this might make your selections a little slower (not allowing jQuery to optimize native browser methods), it's easier to debug and looks cleaner to me than a big string (and hopefully works for you in all browsers).
Reference:
http://api.jquery.com/filter/

Found out that in IE7 and below getElementById is not case-sensitive which was the cause of my problem.
http://msdn.microsoft.com/en-us/library/ie/ms536437(v=vs.85).aspx
In IE8 Standards mode, getElementById performs a case-sensitive match on theID attribute only.
In IE7 Standards mode and previous modes, this method performs a case-insensitive match on both the ID and NAME attributes, which might produce unexpected results.

Related

jQuery selector: Why is $("#id").find("p") faster than $("#id p")

The author of this page: http://24ways.org/2011/your-jquery-now-with-less-suck asserts that the jQuery selector
$('#id').find('p') is faster than $('#id p'), although that presumably produce the same results if I understand correctly. What is the reason for this difference?
Because $('#id').find('p') is optimized to do...
document.getElementById('id').getElementsByTagName('p');
...whereas I'm guessing $('#id p') will either use querySelectorAll if available, or the JavaScript based selector engine if not.
You should note that performance always has variations between browsers. Opera is known to have an extremely fast querySelectorAll.
Also, different versions of jQuery may come up with different optimizations.
It could be that $('#id p') will be (or currently is) given the same optimization as the first version.
It’s browser specific since jQuery uses querySelectorAll when it’s available. When I tested in WebKit it was indeed faster. As it turns out querySelectorAll is optimized for this case.
Inside WebKit, if the whole selector is #<id> and there is only one element in the document with that id, it’s optimized to getElementById. But, if the selector is anything else, querySelectorAll traverses the document looking for elements which match.
Yep, it should be possible to optimize this case so that they perform the same — but right now, no one has. You can find it here in the WebKit source, SelectorDataList::execute uses SelectorDataList::canUseIdLookup to decide whether to use getElementById. It looks like this:
if (m_selectors.size() != 1)
return false;
if (m_selectors[0].selector->m_match != CSSSelector::Id)
return false;
if (!rootNode->inDocument())
return false;
if (rootNode->document()->inQuirksMode())
return false;
if (rootNode->document()->containsMultipleElementsWithId(m_selectors[0].selector->value()))
return false;
return true;
If you were testing in a non-WebKit browser, it’s possible that it is missing similar optimizations.

Setting a property via property or setAttribute

Is one of these more preferable than the other? Why? How about performance--if these are being called thousands of times?
A) element.setAttribute("disabled", true);
B) element.disabled = true;
They both seem to disable an input[text] element in FF 4.
In general…
Use properties. For a long time (until version 7 or 8 IIRC) Internet Explorer had a seriously broken implementation of setAttribute that would set the property not the attribute (the classic point of failure was class since there is no class property (it is className).
In this case in particular… element.setAttribute("disabled", true); is wrong. It should be element.setAttribute("disabled", "disabled");
element.setAttribute("disabled", some_bool) doesn't work like you'd think it will. In particular, standardswise, disabled is what's known as a boolean attribute; its very presence, regardless of its value, makes it true. disabled="", disabled="disabled", disabled="true" and even disabled="false"(!!!) all mean the same thing in most browsers. (Although the last two are actually invalid HTML, most browsers will consider them equivalent to disabled="disabled" for truth purposes. Including every one of the Big Four.) You set a boolean attribute to true by setting a value -- any value, even if it's falsy -- and you set it to false by removing the attribute entirely.
If you care about the actual string value of the attribute (which in this case you shouldn't), and particularly if the attribute isn't already exposed via the DOM (that is, it doesn't have a corresponding property), then use (get/set)Attribute. In most cases (particularly if you care about how it affects the element, like in this case where you're trying to disable an element), use the DOM property.
IE needs some attributes to be set with setAttribute, but not all. I don't have a list though, you just have to check if it works or not. Also, using setAttribute will put the attribute in the DOM, so it can be shown when doing view source.
Only one tip: element.setAttribute('class',xxx) doesnt works in some versions of IE.
Prefer element.className = xxx instead

Why doesn't IE7 think these two strings are equal?

I have an event handler that will remove an element from a list of the corresponding checkbox is unchecked. In the handler for the click event for the checkbox, first I copy the value of the label for that checkbox:
var label = $(this).next().html();
Then, I iterate over the list items and compare each of them to that label:
$("#sortable li").each(function() {
if ($(this).html() === label) {
$(this).remove();
}
});
In Internet Explorer 8 and in Firefox, this works exactly as I'd expect. In Internet Explorer 7, it does not. The condition in the "if" statement is never true. What am I missing here?
By request, the strings being compared are, "Charge" and "Charge" in one case.
Try alert(label) and alert($(this).html()) to see exactly what's being compared.
I'm not seeing any reason to use strict comparison (===), and you could conceivably benefit from using ordinary comparison (==).
As a method of doing what you're saying you want to do, this all seems pretty crazy. I'd always recommend identifying corresponding DOM elements by something more designed-for-the-purpose than their HTML contents, such as an ID.
Why don't you use the Script Debugger and see exactly why that comparison is false? Just set a breakpoint in the if and watch the variables.
The IE8 internal script debugger will help you, just switch to compatibility mode to run the script in the IE7 javascript runtime
Have you tried debugging it? Use alert to see what the two values hold, check that the function is actually called in IE7 and check that they have the same type.
Not an answer to your direct question, but here's another method that uses jQuery's data method to set and read a flag directly on the element:
$(this).next().data("toRemove", true);
//...
$("#sortable li").each(function() {
if ($(this).data("toRemove")) {
$(this).remove();
}
});
Of course, if you were manipulating label at any point then this wouldn't be what you want. I just mention this because .data() is a very useful jQuery feature that I haven't heard much about. I didn't even know about it until a week ago.

INVALID_NODE_TYPE_ERR in jQuery when doing multiple selectors on Chrome

I'm doing a jQuery multiple selector find:
element.find("fieldset, input[type=hidden], input[type=text], :radio")
and in Chrome version 1 it gives this error "INVALID_NODE_TYPE_ERR: DOM Range Exception 2" on line 722 of jquery's selector.js
aRange.selectNode(a);
in context:
function(a, b) {
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
aRange.selectNode(a);
aRange.collapse(true);
bRange.selectNode(b);
bRange.collapse(true);
var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
if (ret === 0) {
hasDuplicate = true;
}
return ret;
}
in this case, a is a HTML hidden input field. From what I can find, it seems to be an issue with the older webkit version, as this error doesn't occur in the new beta of Chrome (probably because it never hits this code because it implements document.documentElement.compareDocumentPosition see selector.js#703).
To step around this problem, I've replaced the multi-selector with four single selects which I merge together which works fine, but it's really ugly:
elements = element.find('fieldset')
.add(element.find('input[type=hidden]'));
.add(element.find('input[type=text]'));
.add(element.find(':radio'));
Is this really the only way around this, or is there something else I can do?
UPDATE There is a thread about this on the Sizzle discussion forum, a possible patch to the Sizzle (jQuery selector) code has been posted, this may find its way into jquery core. It seems to only be an issue when doing a multiple selector on dynamic code
if the problem is the web browser, then sadly there is nothing you can do but wait for an update, or use the multiple selectors and merge the result sets. From what it looks like, this wouldn't be a big performance hit at all, and thus I wouldn't worry about it.
Have you tried...
element.find(['fieldset', 'input[type=hidden]', 'input[type=text]', ':radio'])
?
For reference the entirety of the DOM and rendering is just Apple's WebKit so any bugs you see should be reported to http://bugs.webkit.org -- Chrome doesn't have its own unique engine.

FireFox warning "Unknown pseudo-class or pseudo-element 'hidden' " keeps running over and over

I recently have discovered a warning happening in Firefox that says
Warning: Unknown pseudo-class or pseudo-element 'hidden'
Here is page http://eleven23.net/eleven23/beta/work/web/lounge22.php
And the warning happens when it gets to the part of javascript that has img:hidden
$('img:hidden').eq(0).fadeIn(500);//fades in the hidden images one by one
i++;//add 1 to the count
So Im wondering if anyone has an idea on how to resolve this warning.
Thanks!
The first step is to really stop the repeated calling of doThis() via setInterval which at the moment doesn't happen. Thus the warning appears every 500ms.
Change
$(document).ready (function() {
var int = setInterval("doThis(i)",500);
});
to
$(document).ready (function() {
int = setInterval("doThis(i)",500);
});
Else your call to clearInterval(int) won't do anything as you declared var int twice and try to clear the "outer" int which isn't the interval.
After this fix only 4-5 of this warning should remain in your console.
Now to your error. There isn't much you can do to stop this error from appearing exactly that many times you call doThis().
jQuery uses Sizzle internally as selector engine. And in some cases Sizzle tries to use (on browsers supported) the querySelectorAll() function to find the elements matching your selector.
Now AFAIK is hidden not a valid CSS selector thus although Firefox supports the call to querySelectorAll() it correctly fails after encountering an unknown selector. jQuery catches the error and then does the selection of image:hidden itself.
If you don't want to see this error at all you can use a different jQuery syntax which in this case would stop Sizzle from trying to attempt to use querySelectorAll().
Change
$('img:hidden').eq(0).fadeIn(500);
to
$('img:hidden', $('div#content_wrapper')).eq(0).fadeIn(500);
But I don't advise you to do this as it doesn't really get you much only 4-5 warnings less in your console.
Unfortunately this is a bug within JQuery itself. See: http://docs.jquery.com/Selectors/hidden
Check firebug, even on their example page you get this same warnring. It referes to a non-existing CSS pseudo-class :hidden. Where you are using $('img:hidden')

Categories

Resources