I have the following JavaScript code.
var myCellCollection = document.getElementById('myTbl').cells;
This works well in IE and it returns a collection of table cells. But the same line returns "undefined" in Firefox. I am using IE 9 and Firefox 12.
You should use document.getElementById("myTbl").getElementsByTagName('td');
Stumbled into this today whilst porting an older Internet Explorer app.
Warning:
container.getElementsByTagName('tagname') returns ALL the elements inside container that matches the query tagname.
Thus table.getElementsByTagName('td') will return all td's including those of nested tables!
However table.cells doesn't do that (where implemented).
Also, obviously, it won't match th. So those cells are not in returned collection, optionally leading to the 'problem' of how to resolve their order relative to the td's...
To shim the expected functionality of table.cells (returning both th and td in DOM-order), I wrote the following simple function:
function tableCells(t){
if(t.cells) return t.cells; // use internal routine when supported
for(var a=[], r=t.rows, y=0, c, x; t=r[y++];){
for(c=t.cells, x=0; t=c[x++]; a.push(t));
}
return a;
}
Alternatively, using 'single return' by 'if-else' packs to the same size exactly, yet the above gzips smaller. PS: I tried concat-ting the table.rows[X].cells, but that didn't work (although I wouldn't feel safe doing so anyways)
Usage example:
var identifier = tableCells( /*reference to table (or thead/tbody/tfoot) here*/ );
It will return an array (not a live collection) like the result from table.cells.
Hope this helps someone
Related
Does anyone know of a way to eval a string so that if it (or a function it defines) generates an error, the line and column numbers shown in the stack trace will be offset by an amount specified in advance?
Alternatively, suppose I want to break up a long source string into chunks and evaluate them separately, but still get stack traces that look as though the entire string was evaluated in one go. Is there any way to achieve this effect, except for using empty lines and columns? (I need a browser-based solution, preferably cross-browser, but I can settle for something that works on at least one of the major browsers.)
I don't think is it possible because the underlying mechanism that is assumed working is actually deprecated. For security reasons browsers don't pass the error object to Javascript anymore.
However, since you are working with a custom programming language that gets compiled into Javascript, you know what the structure of the resulting script will be. You could also introduce statement counters in the resulting Javascript, so you can always know what the last thing executed was. Something like:
function(1); function(2);
function(3);
could be translated as:
var __column=0;
var __line=0;
function(1); __column+=12;
function(2); /*__column+=12;*/ __line++; __column=0;
function(3); /*__column+=12;*/ __line++; __column=0;
Where 12 is "function(n);".length.Of course, the resulting code is ugly, but you could enable this behaviour with a debug flag or something.
The best solution I've found so far is to prepend a sourceURL directive to each string before it's eval'ed, giving it a marker in the form of a unique file name in the stack trace. Stack traces are then parsed (using the parser component stacktracejs) and corrected by looking up the line offsets associated with the markers.
var evalCounter = 0;
var lineCounter = 0;
var lineOffsetTable = {};
function myEval(code) {
lineOffsetTable[evalCounter] = lineCounter;
lineCounter += countLines(code);
return eval("//# sourceURL=" + (evalCounter++) + "\n" + code);
}
window.onerror = function(errorMsg, url, lineNumber, column, e) {
var stackFrames = ErrorStackParser.parse(e);
logStackTrace(stackFrames.map(function(f) {
if(f.fileName in lineOffsetTable)
f.lineNumber += lineOffsetTable[f.fileName];
return f;
}));
};
Unfortunately, this only works in Firefox at the moment. Chrome refuses to pass the error object to the onerror callback (a problem which only happens with eval'ed code, strangely enough) and IE ignores the sourceURL directive.
I'm trying to create code that requires the least number of bytes and that works for all browsers including IE 7.
In this example, the program calls dosomething('x1') and dosomething('x2').
If I have code like this:
var items,item,index,count;
items=Array('x1','x2');
count=items.length;
for (index=0;index<count;index++){
item=items[index];
dosomething(item);
}
Could I reduce it to this and have it still function exactly the same in all browsers:
var a=Array('x1','x2'),c=a.length,i;
for (i=0;i<c;i++){
f(a[i]);
}
I understand I changed the variable names and calling function name but my goal is to use the least number of bytes possible in the code to make the code execute.
I'm just not sure if declaring a variable equal to a property of a value from a previous variable in the same list of declarations would actually return correct results.
In other words, does var a=Array('x1','x2'),c=a.length... work, or do I have to specifically do var a=Array('x1','x2');var c=a.length; to make it work in all browsers including IE 7?
This is what the Google Closure Compiler service returned:
var a,b,c,d;a=["x1","x2"];d=a.length;for(c=0;c<d;c++)b=a[c],dosomething(b);
You can find many different Javascript compressors online to automate the process you are hand coding now. Yet, it's always good to understand how they work as it helps to write code that is better compressed.
As for IE, you can test your code by changing the emulations settings in the IE debugger panel. Just press F12, click the Emulation tab, and adjust the document mode to 7 (IE7).
Hope this is enough to get you started in the right direction.
You can use Array.map from IE 9
var items = Array('x1','x2');
items.map(dosomething(item));
When I evaluate the following javascript in Safari 6.0:
Array.indexOf([1,2,3],3)
It returns -1, essentially saying that 3 is not in the array!
But in Firefox it correctly returns 2.
I know that Array.indexOf is a relatively new function so I have code that tests if it is present and if not defines it. However it appears this function is built in to Safari.
Is this a bug, or am I doing something wrong?
Edit: This is actually a simplified version of the problem. What I am actually doing is trying to locate the index of a TD cell in a TR:
var tr = td.parentNode
var col = Array.indexOf(tr.cells,td)
tr.cells does not have an indexOf. But, using Array.indexOf I can treat it as such, at least in Firefox. In Safari this does not work.
I guess in this case I could actually use cellindex, but if that was not defined, how would I go about getting the index?
I'm wondering why you don't use it like intended:
[1,2,3].indexOf( 3 );
Unless it's an academic question. In that case it's actually a bug special Firefox implementation. If you want to use the Array.prototype version directly, you should go like
Array.prototype.indexOf.call([1,2,3], 3);
The Problem
I am converting a relatively large piece of Javascript that currently only works on Internet Explorer in order to make it work on the other browsers as well. Since the code uses XPath extensively we made a little compatibility function to make things easier
function selectNodes(xmlDoc, xpath){
if('selectNodes' in xmlDoc){
//use IE logic
}else{
//use W3C's document.evaluate
}
}
This is mostly working fine but we just came across the limitation that positions in IE are zero-based but in the W3C model used by the other browsers they are one-based. This means that to get the first element we need to do //books[0] in IE and //books[1] in the other browsers.
My proposed solution
The first thought was using a regex to add one to all indexes that appear in the queries if we are using the document.evaluate version:
function addOne(n){ return 1 + parseInt(nStr, 10); }
xpath = xpath.replace(
/\[\s*(\d+)\s*\]/g,
function(_, nStr){ return '[' + addOne(nStr) + ']'; }
);
My question
Is this regex based solution reasonably safe?
Are there any places it will convert something it should not?
Are there any places where it will not convert something it should?
For example, it would fail to replace the index in //books[position()=1] but since IE doesn't appear to support position() and our code is not using that I think this particular case would not be a problem.
Considerations
I downloaded Sarissa to see if they have a way to solve this but after looking at the source code apparently they don't?
I want to add one to the W3C version instead of subtracting one in the IE version to ease my conversion effort.
In the end
We decided to rewrite the code to use proper XPath in IE too by setting the selection language
xmlDoc.setProperty("SelectionLanguage", "XPath");
we just came across the limitation that positions in IE are zero-based
but in the W3C model used by the other browsers they are one-based.
This means that to get the first element we need to do //books[0] in
IE and //books[1] in the other browsers.
Before doing any XPath selection, specify:
xmlDoc.setProperty("SelectionLanguage", "XPath");
MSXML3 uses a dialect of XSLT/XPath that was in use before XSLT and XPath became W3C Recommendations. The default is "XSLPattern" and this is what you see as behavior.
Read more on this topic here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms754679(v=vs.85).aspx
Why not modify the original expressions, so that this:
var expr = "books[1]";
...becomes:
var expr = "books[" + index(1) + "]";
...where index is defined as (pseudocode):
function index(i) {
return isIE ? (i - 1) : i;
}
While debugging some jQuery that is not working in IE, I found this error message:
var item = $("#item_"+ itemArray[itemIndex]).find('a').text().trim();
Object doesn't support this property or method (script.js, line 100, character 2)
The character 2 doesn't make sense to me. Based on the text displayed character 2 would be the letter a in var but of course that doesn't make any sense.
(Should I not use var?)
I know that jQuery is working to some extent or the script would not have been able to get this far on my page.
IE doesn't have String.trim(), you'll need $.trim() (which uses native trim if available, emulates it in IE), like this:
var item = $.trim($("#item_"+ itemArray[itemIndex]).find('a').text());
IE doesn't have a trim method.
Instead, you can call jQuery.trim(...).