Portability of nextElementSibling/nextSibling - javascript

I'm currently writing an accordion and running into the same problem as described in nextSibling difference between IE and FF? - specifically differences between Microsoft's nextSibling / nextElementSibling and that implemented by everyone else.
For various reasons, using jquery is not an option. Nor is getting all my MS users to upgrade to MSIE9
Currently I'm using the following code to work around the problem:
// tr is a TR doc element at entry....
while (nthRow--) {
// for Chrome, FF tr=tr.nextElementSibling; for MSIE...tr=tr.nextSibling;
tr=tr.nextElementSibling ? tr.nextElementSibling : tr=tr.nextSibling;
if (!tr || tr.nodeName != "TR") {
break;
}
tr.style.display="";
}
Which seems to do what I expect in MSIE6, FF and Chrome - i.e. the nthRow TR elements below the initial TR are made visible (previously style.display="none").
But is this likely to have unexpected side effects?
(I'm a bit of a newbie with Javascript ;)

nextSibling will see HTML code comments, so be sure to keep them out.
Other than that you should be alright since you won't have any text nodes between your tr elements.
The only other issue I could think of would be in Firefox 3 where nextElementSibling hadn't yet been implemented. So if you're supporting that browser, you'll need to manually emulate nextElementSibling. (Pretty sure they had it implemented in FF3.5 though.)
You'll be safer to create a nextElementSibling() function:
tr = tr.nextElementSibling || nextElementSibling(tr);
function nextElementSibling( el ) {
do { el = el.nextSibling } while ( el && el.nodeType !== 1 );
return el;
}

Considering previous answers, I am currently implementing it this way to grant cross-browser compatibilty:
function nextElementSibling(el) {
if (el.nextElementSibling) return el.nextElementSibling;
do { el = el.nextSibling } while (el && el.nodeType !== 1);
return el;
}
This way, I can avoid the do/while loop for browsers that support nextElementSibling.
Maybe I'm too scared of WHILE loops in JS :)
One advantage of this solution is recursability:
//this will always works:
var e = nextElementSibling(nextElementSibling(this));
//this will crash on IE, as looking for a property of an undefined obj:
var e = this.nextElementSibling.nextElementSibling || nextElementSibling(nextElementSibling(this));

Firefox nextSibling returns whitespace \n while Internet Explorer does not.
Before nextElementSibling was introduced, we had to do something like this:
var element2 = document.getElementById("xxx").nextSibling;
while (element2.nodeType !=1)
{
element2 = element2.nextSibling;
}

Related

document.all is not working in Firefox

I am working on an old project for maintenance. I found that document.all is not working in Firefox 25. And I am getting the error below.
TypeError: document.all.TabularDataControlAttivitta.object is undefined
And my sample code is:
document.all.TabularDataControlProj.object.Filter = 'COMPANYCODE=' + compValue;
document.all.TabularDataControlProj.object.Reset();
document.getElementById('cmbActivity_' + rowNo).options.length = 1;
if (document.getElementById('cmbProject_' + rowNo).options.length > 0) {
for (var i = document.getElementById('cmbProject_' + rowNo).options.length - 1; i >= 0; i--) {
document.getElementById('cmbProject_'+rowNo).options[i] = null;
}
}
if (document.all.TabularDataControlProj.recordset.recordcount > 0) {
document.all.TabularDataControlProj.recordset.movefirst;
}
pOption = new Option('-Select-', -1);
document.getElementById('cmbProject_' + rowNo).add(pOption);
while (document.all.TabularDataControlProj.recordset.eof == false) {
Optionp = new Option((document.all.TabularDataControlProj.recordset.fields(0) + ' - ' + document.all.TabularDataControlProj.recordset.fields(2)), document.all.TabularDataControlProj.recordset.fields(0));
document.getElementById('cmbProject_' + rowNo).add(Optionp);
document.getElementById('cmbProject_' + rowNo).selectedIndex = indxAct;
document.all.TabularDataControlProj.recordset.movenext;
}
}
Any patch or solution for this? Because it's very difficult to edit the entire project.
document.all is non-standard. It was a Microsoft-specific feature that they added to IE. Most other browsers have never supported it.
Even in IE it is now deprecated. You should not be using it.
(your old project must be very old, because this has been the case for quite some time now)
For most cases, you can use document.getElementById() instead.
If you're using document.all to get an element using it's ID value then document.getElementById() is a direct replacement.
If you're using document.all to get an element some other way, then I recommend switching to getting it by ID (add an ID if necessary).
I note that the way you're using the element makes it look like it may be an activeX control? (ie I see stuff like .object.Filter, .recordset.movefirst, etc)
If that is the case, then you need to be aware that Firefox does not support activeX controls at all. They are also specific to IE. If it is an activeX control and you need it to work in Firefox then unfortunately, you probably have quite a lot of rewriting ahead of you.
Document.all
Provides access to all elements with an id. This a legacy non-standard interface, you should use the document.getElementById() method instead.
Ref: https://developer.mozilla.org/en/docs/Web/API/Document
As the error states, the problem is not with document.all not working (it does), it's with document.all.TabularDataControlAttivitta.object being undefined. This could be either because of application-specific reasons (you simply don't define an object expando), or because you have several elements with name or id equal to TabularDataControlAttivitta.

jQuery that works in Firefox but not in IE

Ok guys/girls.
Below is some jQuery that runs in Firefox but no IE. I have no idea why it craps out in one and not the other.
Anyone??
function SwapTextAssetforPOS() {
$("*").each(function () {
if ($(this).children().length == 0) {
$(this).text($(this).text().replace('Assets', 'POS'));
$(this).text($(this).text().replace('Asset', 'POS'));
$(this).text($(this).text().replace('assets', 'POS'));
$(this).text($(this).text().replace('asset', 'POS'));
}
});
}
Sorry folks - the error that I get is:-
SCRIPT65535: Unexpected call to method or property access.
jquery-1.6.min.js, line 16 character 60352
EDIT:------------------------------------------------------------------------------------
Ok so an update - I removed the * selector and IE no longer blows up, my issue now is that I cant figure how to get it to do the replace on the element. I have the following code to ping up all the text elements in the object:
function SwapTextAssetforPOS() {
var containerElementByID = $("#assetDetailContents");
containerElementByID.children().children().each(function () {
var $this = $(this);
alert($this.text());
});
This chucks me up an alert for every bit of text, however some is contained within a table, some is within a span, and some is just there. I have no control over a majority of this stuff so my new question is how do I get the previous replace to work using this type of selector. -- I can believe how painful this is..
Cheers again
I see the problem in my IE browser. When you do the $("*").each... it takes every single element in the page (including title, script, etc). When you do .text(), looks like it fails for some elements in IE for which .text() doesn't make sense. Replace "*" for "div" and it should work for the divs for example.Maybe you could do something like if ($(this).text()) {$(this).text($(this).text().replace('Assets', 'POS'));} to make sure the text() is defined for that element.
Still, going through the whole DOM is overkill. Can you add a class to the elements that can have the text?, like class="replaceable" so you could just do a $(".replaceable").text(...
Ok folks - so firstly thanks for the help.
I have resolved the issue by cobbling a number of suggestions together and by doing a little bit of investigative work.
In a nutshell IE was crapping out when it ran up against an tag. I no not why but this is where it fell over every time.
function SwapTextAssetforPOS() {
var overlaycon = $("#jq-selectionHelper").find("*:not(img)"); //This line simply looks at the div surrounding the template and returns (to an array I believe) every element therein except for img tag
//as this breaks in IE when tying to do the replace text stuff below.
overlaycon.each(function () {
var $this = $(this);
if ($this.children().length == 0) {
$this.text($(this).text().replace('Assets', 'POS'));
$this.text($(this).text().replace('Asset', 'POS'));
$this.text($(this).text().replace('assets', 'POS'));
$this.text($(this).text().replace('asset', 'POS'));
}
});
}
This code runs and I believe is a lot more efficient than my original offering. Any further suggestions for performance re-factoring are welcome but thank the lord this is now working.
Thanks again for all the help.
Regards
This code has no problem as seen here . I tested in IE and FF both works fine
http://jsfiddle.net/wKWRC/
You should use F12 developer tool for IE and see what error you are getting that way others can know what exact problem is . You can debug script and see where you are getting error . IE is sensitive to javascript errors and its possible there is some error before you are calling this function .
http://msdn.microsoft.com/en-us/library/gg699336%28v=vs.85%29.aspx
Just a hunch but you might be running out of memory in IE.
First, using $('*') is never advisable, its better that you narrow it down with a selector like $('p').
Also, every time you call $(this) you create a new jQuery object, so if there are a lot of elements on your page you're making 9 objects every time.
The convention is to set $this = $(this) at the begining of the function so you only use one object.
function SwapTextAssetforPOS() {
$("*").each(function () {
var $this = $(this);
if ($this.children().length == 0) {
$this.text($this.text().replace('Assets', 'POS'));
$this.text($this.text().replace('Asset', 'POS'));
$this.text($this.text().replace('assets', 'POS'));
$this.text($this.text().replace('asset', 'POS'));
}
});
}
try it like this
for (var i = 0, replacements = ['Assets','assets','asset','Asset']; i < 4; i++)
$("*:contains('" + replacements[i] +"')").map(function() { this.innerHTML = this.innerHTML.replace(/asset(s){0,1}/igm, 'POS'); })

why if (element.innerHTML == "") is not working in firefox

why is if (element.innerHTML == "") not working in firefox
but works fine in IE , any ideas please ?
Hard to say without seeing your HTML, but I'd say probably because you have some empty white space in the element, and IE doesn't treat that as a text node, while FF does.
I believe it is actually a more strict standards compliance to treat any empty white space between tags as a text node, but IE doesn't comply.
You could do:
var htmlstring = element.innerHTML;
// use the native .trim() if it exists
// otherwise use a regular expression
htmlstring = (htmlstring.trim) ? htmlstring.trim() : htmlstring.replace(/^\s+/,'');
if(htmlstring == '') {...
Or just get rid of the whitespace in your HTML markup manually.
An alternative method to check for the empty string is to check the length:
element.innerHTML.length == 0
But, you'd still have to trim() if you had a whitespace string you wanted to match.
You could check if element.innerHTML.trim() == "" for the best results. However, then you have to extend the string prototype with a trim() method:
if (!String.prototype.trim) {
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, ''); };
}
if (element.innerHTML.trim() == "") {
//do something
}
For me, it seemed like setting my innerHTML was not working in Firefox nor Chrome but it did work in IE because of my error. It turned out that I was never getting the element using getElementById in the first place. IE seems to do just fine with elements which are defined with name= with getElementById but Firefox and Chrome was more stringent and accepts only id= elements. (More correctly in my view)
I hope this saves somebody some frustration.
Why does IE do these sorts of things and confuse people...

Works in firefox but not IE8

This is working fine in firefox but only closes the first page and then breaks in IE8. Firebug in IE8 says that x.item(o) is null. I can't figure out why this works in firefox but not IE. Thanks for any help.
pager(x=document.getElementsByName("pg1"));
function pager( x ) {
var curr = document.getElementById('showing');
$(curr).fadeOut('fast');
curr.id = 'hide';
$(x).fadeIn('slow');
x.item(0).id ='showing';
}
if(x.item(0).id = NULL )
That's an assignment. You wanted == for comparison.
(What's NULL in capital letters? An element's id property won't be null; if it's not set, it'll be an empty string.)
It seems to me you'd be better off using jQuery's toggle method.

Get offsetTop & IE

I am trying to get the offsetTop in IE but this simply doesn't work like I want to it.
var miniMap = $('#miniMap'),
parnts = miniMap.parents(),
l = parnts.length,
i, setVpos,
setHPos = $('#showMap').position().left;
for (i = 0; i < l; i += 1) {
if (parnts[i].className === 'smallBox') {
if (isIE) {
setVpos = parnts[i].offsetTop; // returns the wrong value
}
else {
setVpos = parnts[i].offsetTop; // works as it should
}
}
}
As you can see I am using jQuery. I could of course use the jQuery offset/position().top && .each:
parnts.each(function() {
if ($(this).hasClass('smallBox')) {
setVpos = $(this).position().top;
} ....
.. BUT, I've heard ".each" is much slower than using a for loop. Also I am really curious and would love to know what you guys used before jQuery :-)
My problem is when doing it with a for loop I can't use the jQuery obj => position().left won't work.
I've Googled and found the following:
document.getElementById("elementID").offsetLeft // all other
document.all.elementID.offsetLeft // IE only
I figured perhaps this could work:
if (isIE) {
setVpos = document.all.parnts[i].offsetTop;
}
.. but it didn't. So now I'm asking you guys for help instead.
Any suggestion? Thanks on beforehand!
You clearly have a reference to the jQuery library, so why aren't you using it to get the offset? Using jQuery, you should have no need for any variables such as isIE. The biggest benefit of using a library is cross-browser compatibility. Here's how you could do it...
var off = $(parnts[i]).offset();
alert(off.top);
offset on jQuery API
I think you'll find that IE gives a very different answer when offset() is used as opposed to other browsers . Perhaps you should try to run you code before you psot an answer.

Categories

Resources