JavaScript Archive
Safari Bug: wrong offsetTop on TD
- 2009-03-20 (Fri)
- JavaScript
# EDIT: This bug will be fixed soon. See WebKit Bugzilla 19094. It’s fixed already on Nightly. If you do have a tweak for this bug, (such as me) you need to add another sniff like follows.
var webkit = indow.navigator.userAgent.match(/WebKit\/([0-9]{3})/);
if (webkit && parseInt(webkit[1]) < 528) {
...
}
Most of JavaScript library comes with a method that returns pixel position of the given element. For example, jQuery has jQuery.offset(), MochiKit has MochiKit.Style.getElementPosition(), MooTools has Element.getPosition(). They work well in most cases, but today i found the situation that doesn't work and that, I think, is a Safari's bug.
Before going into detail, here's the condition that pixel position getter method doesn't work.
- The browser is Safari
- The element you try to work on is TD (or display:table-cell)
- It set vertical-align NOT as top (maybe middle or bottom)
Here I made examples for this case: Safaribug: wrong offsetTop on TD. When you click each table cell, it will return the pixel position of the clicked TD. Open the page with Safari and other browsers. There are differences.
How it happens? Let me introduce how JS lib calculate the pixel position of any given element. I found a nice code with comments on Google doctype project.
PageOffset: How to calculate the position of an element on the page (goog.style.getPageOffset)
It's really step-by-step good explain, you should go through with it. Here I extract how Safari runs the code:
function getPageOffset(el) {
var pos = {
x: 0,
y: 0
};
var viewportElement = document;
if (el == viewportElement) {
return pos;
}
var parent = null;
var box;
pos.x = el.offsetLeft;
pos.y = el.offsetTop;
parent = el.offsetParent;
if (parent != el) {
while (parent) {
pos.x += parent.offsetLeft;
pos.y += parent.offsetTop;
parent = parent.offsetParent;
}
}
if (el.style.position == 'absolute') {
pos.y -= doc.body.offsetTop;
}
parent = el.offsetParent;
while (parent && parent != document.body) {
pos.x -= parent.scrollLeft;
parent = parent.offsetParent;
}
return pos;
};
The problem happens on the very first access to el.offsetTop on line 15. It should be 0, since it's a table cell and no padding/margin/border set, but it seems Safari returns the vertical offset to the descendant element on that property.
I don't come up with any smart way to solve it... so I just tweak it.. Please WebKit, fix this bug!!
- Comments: 0
- Trackbacks: 0
JavaScript: The Good Parts Talk available on Google Tech Talks
- 2009-03-04 (Wed)
- JavaScript
A few times, I mentioned on my blog here, “JavaScript: The Good Parts.” The author of the book, Douglas Crockford gave a talk at Google Campus on Feb 27, 2009. You can check it out on YouTube.
The talk is very insightful. And it actually very much about what he wrote on the book. If you are interested in reading that book but haven’t got that yet, you may get at least half of his points by watching this Google TeckTalk. And also he is touching what the future of JavaScript will be. Strict mode will be interesting. And I guess it would boost the speed.
I pick up some of his points what I was interested in.
- Comments: 0
- Trackbacks: 0
False となる値 – JavaScript: The Good Parts
- 2008-12-26 (Fri)
- JavaScript
“JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス“の Appendix A に収録されている(であろう)表を引用しています。
False となる値
以下にあげる値は、if や switch の条件式の中で、false となります。
| Type | Value |
|---|---|
| number | 0 |
| number | NaN |
| string | ""(要素が空(から)の文字列) |
| boolean | false |
| Object | null |
| undefined | undefined |
意識/無意識のうちにでも理解しているとは思いますが、こうやって整理されているとまたわかりやすいですね。
さてここからは余談です。多くの言語では、文字列と配列は非常によく似た存在です。つまりどちらであっても、長さというプロパティがあって、添字によって中のデータに一つ一つアクセスできる、というものです。もし要素が空(から)の文字列が false であるなら、要素が空の配列はどうでしょう?実際には要素が空(から)の配列は false とは評価されません。おそらく JavaScript の Array は Object に帰するもので、空配列は null でない Object と云う意味で、例外になってしまうからでしょうか。
- Comments: 0
- Trackbacks: 0
Falsy values – “JavaScript: The Good Parts”
- 2008-12-26 (Fri)
- JavaScript
This entry is a note from “JavaScript: The Good Parts” on page 106, Appendix A: Awful Parts.
Falsy Values
In JavaScript, following values are considered as “false” when they come to conditional expression.
| Type | Value |
|---|---|
| number | 0 |
| number | NaN |
| string | ""(empty string) |
| boolean | false |
| Object | null |
| undefined | undefined |
You must have understood them already, but it’s good to have it organized.
In most of languages, String and Array is very similar. I mean, both instances have a length property for nature and you can access stored values by their own indexes. So I was wondering, if an empty string is falsy, why not an empty array is? No, an empty array is not falsy. I guess its because an array is acutally an object. Try typeof operator with one.
Here, I tried to evaluate an array with JavaScript’s nature, duck typing, but well, it doesn’t work as I imagined.
var a = []; console.log( a ? 'true' : 'false'); //of course, true; console.log( (String.prototype.constructor.apply(a)) ? 'true' : 'false'); //Oh? false... so..? var b = ['b']; console.log( (String.prototype.constructor.apply(b)) ? 'true' : 'false'); //still false...
Any value, even if it’s a string type of a String wrapper object, passed to string constructor makes an empty string.
String.prototype.constructor(['a']);
will do more as what you can imagine. String.prototype.constructor will run .valueOf() on the argument.
- Comments: 0
- Trackbacks: 1
文字列比較 – JavaScript: The Good Parts
- 2008-12-23 (Tue)
- JavaScript
いつだったか、この blog にも何度か登場している Mauvis に勧められて購入した “JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス” は原書で何度か読みました。なんだか翻訳が最近出たそうなのですが(訳の問題はわかりませんが)、これはお勧めです。すごく薄いけど、気付かされることがすくなくとも、僕には沢山ありました。しばらくこの本から学んだことをいくつか紹介していきたいと思います。
最初は String の比較です。
以下の expression は共に true と評価されます。
('cat' == 'c' + 'a' + 't') // true
('cat' === 'c' + 'a' + 't') // true
この本を読むまでは、JavaScript はなんとなくインスタンスベースの比較をしてるのかなぁと思ってました。それはつまり、等号( == あるいは === )の左右両辺にが全く同じメモリアドレスを指し示している時だけ、true と評価される、ということです。しかし実際には、String の比較時はその文字列の内容を評価していました。
var s1 = s2 = 'foo'; var s3 = 'foo'; s1 == s2; // true s1 == s3; // still true, of course.
従って、もし万が一、インスタンスで文字列を比較したい場合は、以下のようにするといいでしょう。
var s4 = new String('foo');
var s5 = new String('foo');
s4 == s5 //false
しかし本の中になんども登場する通り(そして僕も強く同意しますが)、Wrapper Object (String, Number そして Boolean) は混乱の元です。必要となるシチュエーションもあまり想像できません。
ところでインスタンスベースの文字列比較、というのはもうあまりポピュラーではないんでしょうか?僕が多分最初に Java を学んだときはインスタンスベースの比較だったような気がしたのですが、今試してみたら (1.5.0_16 on Intel Mac) 内容で比較していますね。PHP も同じようです。
- Comments: 0
- Trackbacks: 0
String comparison – “JavaScript: The Good Parts”
- 2008-12-23 (Tue)
- JavaScript
I’ve read “JavaScript: The Good Parts” a few times already, since it’s a thin but dense book. This book is somehow basic, but full of discerning. I should have do an Advent Calendar-ish thing if I could start this earlier.. But anyway, I will keep writing a bit about what I learned from this book for a while.
So the first thing, is a string comparison, on Chap. 2, page 10.
Following expressions both return true!
('cat' == 'c' + 'a' + 't') // true
('cat' === 'c' + 'a' + 't') // true
Before I read this, I originally thought JavaScript might compare the instance-base equality. I mean if the string instances on both side on the equal sign were pointing to the exactly same memory address, it would return true, but it is actually comparing its value.
var s1 = s2 = 'foo'; var s3 = 'foo'; s1 == s2; // true s1 == s3; // still true, of course.
So, if you wanna do instance base equality, do as follows:
var s4 = new String('foo');
var s5 = new String('foo');
s4 == s5 //false
But as it’s mentioned in the book (and I strongly agreed!), wrapper objects (String, Number and Boolean) are just seeds of confusion. I don’t also see the neccessity of using them.
By the way, I thought that instance-base comaprison is more popular rather than value-base comparison. But as far as I just tested, Java (1.5.0_16 on Intel Mac) compares value, PHP compares value.. that’s intereting.
- Comments: 0
- Trackbacks: 0
Coway’s Game of Life
- 2008-11-14 (Fri)
- JavaScript
ライフゲームは思い入れがあるというか、僕が NYU で一番最初に自分で書き上げたプログラムです。セル・オートマトンというアイデアもとても面白いし、何より動いていくその様が大好きでした。ITP の最初の期末、発表の数時間前に書き上げて、動いていたのはとても感動的…なんて話はどこかに書いた気がします。どうってことないプログラムだけれど、やっぱり動くのをみるのは気持ちいいですよね。その発表したバージョンは、Macromedia Director Lingo で書かれていて、どうやったか覚えてないけど、マルチプレイヤーでした…3人まで参加できて、matrix のアニメーションを排他ロックしていたはず…
マルチプレイヤーの部分はまだ実装してませんが、一人遊びをする分にはこれで十分かと思います。お楽しみください。100分の1程度の割合で、セルが突然変異をするので色がだんだんかわっていきますよ。
インタラクションの制御に MochiKit を使っていますが、もうちょっと改良の余地はありそうです。
- Comments: 0
- Trackbacks: 0
モチキット入門
- 2008-11-13 (Thu)
- JavaScript
諸般の事情により、今更ですが MochiKit をはじめてみました。Prototype.js と jQuery にどっぷりだった人が全然違う vocabulary にすごい悪戦苦闘してる感じ。ただしばらく使ってみると、それなりに使いやすいのは解ってきたので、ごく初歩でつまずいたところを簡単に羅列しておきます。
- Mochikit ではなく MochiKit
このへんは、キャピタルレター好きのドイツ人感性だろうか… - MochiKit.js はローダー。
意味論的にメソッドがファイル (object) に分類されていて、MochiKit.js を読み込むと凡てのファイルを読み込む。選択的に使いたい場合は、<script>文を複数行書く。ただし、依存関係もあるので、MochiKit.MochiKit.SUBMODULES に書かれた順序で読み込むのが正しい感じ。 - global 汚染しない方法もある
jQuery な人が最初に気持ち悪いと思うのは、デフォルトでかなりの数の関数が global 名前空間に登録されていること。official のドキュメントの一番最初にあるが、<script type="text/javascript">MochiKit = {__export__: false};</script>を MochiKit 関連を読み込む前に発行しておけば、FQMN (Fully-Qualified-Method-Name: なんて言い方があるかどうか知らないが) のみのアクセスに制限できる。 - ドキュメントは expand all してページ内検索
語彙がだいぶ違うとはいえ、宇宙語ではないので多分全ての語彙からキーワードを検索すれば多分何かは解るはず。ドキュメントは Overview が時代遅れな感じがしますが、細かい部分の完成度は高いです。
前に作った、Conway’s Game of Life を MochiKit を使って書き直してみました。というか、EventRunner を使わなくなっただけとも言えます…
- Comments: 0
- Trackbacks: 0
Duck Typing on JavaScript, and getter/setter
- 2008-10-31 (Fri)
- JavaScript
My friend Mauvis brought me (ahem) an interesting problem again.
Write a one-line piece of JavaScript code that concatenates all strings passed into a function:
function concatenate (/*any number of strings*/) {
var string = /*your one line here*/
return string;
}
So, the first thing I’ve tried was as follows:
function concatenate () {
var string = (function (a) {var r = [];for (var i = 0, len = a.length; i < len; ++i}{ r[i] = a[i]}return r.join('');)(arguments);
return string;
}
I know that doesn't look good. And it's just in one line, but it's actually a few lines.
Then Mauvis replied me back..
function concatenate () {
var string = function(a){return Array.prototype.slice.call(a).join('');}(arguments);
return string;
}
and eventually it bacame like this.
function concatenate () {
var string = Array.prototype.join.call(arguments, '');
return string;
}
This is a pretty interesting topic in JavaScript. The first one I did is pretty straight forward, right? Arguments object is an array-like object, that each local (function) space has automatically. You can iterate like an array. So I loop through each value, put it all together in a different array, then construct a new string.
The second one and third one are a bit unusual for the first look. What it does is apply Arguments object as an array in order to call slice or join method, which both of them are originally Array's prototype methods.
If you are fuzzy about it, Let's take a look the specification of JavaScript.
It's a little too long, but I quote an entire section of slice (Page 105 - 106) here.
15.4.4.10 Array.prototype.slice(start, end)
The slice method takes two arguments, start and end, and returns an array containing the elements of the array from element start up to, but not including, element end (or through the end of the array if end is undefined). If start is negative, it is treated as (length + start) where length is the length of the array. If end is negative, it is treated as (lengt+ end) where length is the length of the array. The following steps are taken:
- Let A be a new array created as if by the expression new Array().
- Call the [[Get]] method of this object with argument "length".
- Call ToUint32(Result(2)).
- Call ToInteger(start).
- If Result(4) is negative, use max( (Result(3) + Result(4)), 0 ); else use min( Result(4), Result(3) ).
- Let k be Result(5).
- If end is undefined, useResult(3); else use ToInteger(end).
- If Result(7) is negative, use max( (Result(3) + Result(7)),0 ); else use min( Result(7), Result(3) ).
- Let n be 0.
- If k is greater than or equal to Result(8), go to step 19.
- Call ToString(k).
- If this object has a property named by Result(11), go to step 13; but if this object has no property named by Result(11), then go to step 16.
- Call ToString(n).
- Call the [[Get]] method of this object with argument Result(11).
- Call the [[Put]] method of A with arguments Result(13) and Result(14).
- Increase k by 1.
- Increase n by 1.
- Go to step 10.
- Call the [[Put]] method of A with arguments "length" and n.
- Return A.
The length property of the slice method is 2.
NOTE
The slice function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the slice function can be applied successfully to a host object is implementation-dependent.
And here's join section (page 103). This one is snippet.
15.4.4.5 Array.prototype.join (separator)
The elements of the array are converted to strings, and theses trings are then concatenated, separated
by occurrences of the separator. If no separator is provided, a single comma is used as the separator. The join method takes one argument, separator, and performs the following steps.....(snip)...
NOTE
The join function is intentionally generic; it does not require that its this value bean Array object. Therefore, it can be transferred to other kinds of objects for use as a method. Whether the join function can be applied successfully to a host object is implementation-dependent.
Both sections consist of definition, procedures, and note. And interesting thing is on the Note: intentionally generic and implementation-dependent. So it depends on Browser dev, to implement it generic or not..
I made a page to try out those things: Duck Typing test.
And acutally, as it's written on SPEC, you can use slice/join for your own object. What it's written on 2 and 14 on Array.prototype.slice procedure, it needs to access [[Get]], which is an internal method for each object (page 26 - 28). According to John Resig's JavaScript Getters and Setters, it can be used in Firefox, Safari and Opera. But as far as I tried, it doesn't work in Opera. If IE has the other way to do it, I will be more interested in...
- Comments: 4
- Trackbacks: 0
Process String
- 2008-10-28 (Tue)
- JavaScript
My good friend Mauvis was writing on his blog about to convert a string to comma separated ascii numbers. I commented there about his code. Here’s how I wrote, and also see his original: String.toCharCode().
String.prototype.toCharCode = function()
{
var str = this.split('');
var len = str.length;
var work = new Array(len);
for (var i = 0; i < len; ++i)
{
work[i] = String.charCodeAt(str[i]);
}
return work.join(',');
}
It was his quick hack for the day, I suppose, but it turns out to be very interesting thing to me. There are a few points to write faster code.
- explicit length of for loop — dot access considered harmful at least for speed.
for JavaScript, .(dot) access is always very slow. Some people do like...for (var i = 0; i < arr.length; i++). That's not good. On this case, i need to use array length twice so I made a local variable. The local variables are always the fastest access. - NO for...in for Array
for...in is basically built for Object enumeration. By using for...in, JavaScript will pass all properties with the internal DontEnum attribute false, to i (or accessor whatever you name). MDC:Object.propertyIsEnumerable() is very interesting to read about this topic. - Do not concatenate a string
In most of languages, string is immutable. In my early life of programming, I always wondered.. what's the hell? Immutable? As its meaning, string is not changeable. but let's see like...var s = "foo"; s = "bar"; alert(s); // returns "bar"!
right? You can change it. But as a lower process level point of view, the code above is same exact meaning as follows.
var s = new String("foo"); s = new String("bar"); alert(s); // returns "bar"!Two instantiation process. Now you believe me, right? This article: HotRubyがC Rubyより速い本当の理由は? (written in Japanese) explains very comprehensively how JavaScript engine would implement it. At least you should take a look at this picture. But overall, String concatenation considered harmful. In most cases, you can use Array.join() to connect multiple strings.
These looks very minor and small changes. But it does make a difference.
- Comments: 0
- Trackbacks: 1
- Search
- Feeds
- Meta
- Links
- Ads!
-
