タグ「Tips」による検索結果 28

Search Form

JavaScript: ビット反転とString#indexOf

if(~block.innerHTML.indexOf(this._key))
      return;
hatebu - ’ellaneous

これは使える。


// 含むとき
if (~str.indexOf("http://")) {}

// 含まないとき
if (!~str.indexOf("http://")) {}

+1だと後者に括弧が1組増えるとか、含む含まないの判定なのに+1?みたいな違和感があるけど、これだとけっこうすっきりする。

慣れがいるかもだけど。

JavaScript: Iterator関数をhasOwnProperty代わりに使う

いちいちhasOwnPropertyを使わなくてよくする(ジェネレータの使いかた) - 素人がプログラミングを勉強するブログ

多分Firefox2/3限定。JavaScript1.7相当。

Iterator関数を噛ませる方法もあります。

Object.prototype.extend = function(){};

var o = {a:1,b:2,c:3};
for (var i in o) console.log(i);
/* ->
a
b
c
extend
*/

for (var i in Iterator(o)) console.log(i);
/* ->
["a", 1]
["b", 2]
["c", 3]
*/

for each (var i in Iterator(o)) console.log(i);
/* ->
["a", 1]
["b", 2]
["c", 3]
*/

こんな具合になってるので

for (var [i,v] in Iterator(o)) console.log(i);
/* ->
a
b
c
*/

for (var [i,v] in Iterator(o)) console.log(v);
/* ->
1
2
3
*/

[p[0] for (p in Iterator(o))]
// -> ["a", "b", "c"] keys

[p[1] for (p in Iterator(o))]
// -> [1, 2, 3] values

とか。

微妙に形が変わるのが微妙といえば微妙。


yieldもletも使ってないので、Firefox2のFirebugコンソールでも使えるあたりが強み。

あとArrayでも順序が保証されている(はず)なのでbreakの効くforEachとしても使える。

関連リソース

Iterators and Generators - MDC

One advantage of using Iterator() to access the contents of an object is that custom properties that have been added to Object.prototype will not be included in the sequence.

Iterator() can be used with arrays as well:

JavaScript 1.8 では配列が自動的に Iterator に変換されなくなった? - やんばるもじら

JavaScript: document.evaluate、nodeValue、デフォルト名前空間

$gを更新した。

それ絡みで気付いたことのメモ。

nodeValueとtextContent

tmp = xp.snapshotItem(i);
ret[i] = tmp.nodeValue || tmp.textContent;

第3引数にtypeofがobjectとなる引数を与えた場合の挙動を改良。


DOM:element.nodeValue - MDC

//a/@hrefのようにしてAttribute Nodeを集めたとき、それらのnodeValueプロパティを参照すると属性値(href属性の値)が得られる。

Element Nodeのときはnullになるので、この場合はtextContentをみる。

$g('//a', document, {})
// -> アンカーテキスト
//    ["mayokara note", "Older >", ...]
$g('//a/@href', document, {})
// -> href属性値
//    ["http://mayokara.info/note/", "http://mayokara.info/note/1", ...]

で、こういうことができる。

ただしa要素のhrefプロパティから取得した場合とは違い、Absolute URIにはならない。

Relative URIで書かれているものは、そのまま。

デフォルト名前空間について

var doc = context.ownerDocument || context, defaultNamespaceURI = (/xml$/).test(doc.contentType) ? doc.documentElement.getAttribute("xmlns") : "";
var resolver = function(prefix){
    return doc.createNSResolver(context).lookupNamespaceURI(prefix) || defaultNamespaceURI;
};

XHTMLのみを対象とするのならhttp://www.w3.org/1999/xhtml決め打ちで良いのだけど、RSS 1.0を(GM_)XMLHttpRequestで取得してDOMParserに食わせたものにXPathを使いたいときはhttp://purl.org/rss/1.0/を返す必要がある。

Atomであればhttp://www.w3.org/2005/Atom

そういうときのための修正。


デフォルトリゾルバは XML 文書のデフォルト名前空間を処理しません。

Introduction to using XPath in JavaScript - MDC # XML 文書のデフォルト名前空間を実装する

createNSResolverはdcやらrdfやらに引っ掛らなかったときにデフォルト名前空間のURIを返さない。nullが返る。

なので、documentElementのxmlns属性を見て、それを返すようにする。

namespaceURIプロパティは、RSS 1.0のようにdocumentElementのnodeNameがrdf:RDFのときxmlns:rdfのURIを返してくるので使えない。


デフォルト名前空間を含め、名前空間の接頭辞は宣言している要素とその子孫で有効ですが、子孫要素で同じ接頭辞(あるいはデフォルト)の名前空間が宣言されると、その内部では新しい宣言によって結びつきが上書きされます。

XML名前空間の簡単な説明 # デフォルト名前空間

厳密にはcontextから(取得されるノードから?)遡って調べなければならないのだけど、手を抜いた。

やるとすれば、contextの兄弟ノードとして適当なノードをinsertして、それのnamespaceURIプロパティを取るのが楽そう。


ついでに、きちんとcreateNSResolverを使うようにした(dc:dateなどのため)。

余録:Firefox2のDOMParser

RSS 1.0を食わせたとき、documentElementの子要素以下のノードが取れない。

Bugzillaにあたってみたけど、探すのに不得手なせいで見つけられなかった。

Firefox3だとちゃんと取れる模様。

JavaScript: コマンド文字列からC言語のargv相当を取得する

Core JavaScript 1.5 Reference:Global Objects:String:split - MDC

String.prototype.splitの罠 - 素人がプログラミングを勉強するブログ

String#splitの挙動と後方参照を利用すればできそう。

matchだとgフラグ立てたとき括弧の中身まで取れない。

正攻法だとRegExp#execでgフラグ立ててwhileなんだろうけど、while使いたくないし一時変数も用意したくないし1個1個pushとかもいや。

yフラグも同上。

var str = "grep \"ho\\\"ge\" 'fu\\'ga' piyo \"hello work!\"";
str
// -> "grep "ho\"ge" 'fu\'ga' piyo "hello work!""
str.split(/(["']?)((?:\\.|[^\\])*?)\1(?:\s+|$)/)
   .filter(function(v,i){ return (i%3 === 2); })
   .map(function(v){ return v.replace(/\\(["'\\])/g, "$1"); })
// -> ["grep", "ho"ge", "fu'ga", "piyo", "hello work!"]

追記

$ echo he\llo
hello
$ echo "he\llo"
he\llo
$ echo he\"llo
he"llo
$ echo 'he\"llo'
he\"llo

Cygwinでやってみたんだけど、何か上のじゃだめっぽい……

JavaScript: 継承(attach、from)

JavaScript: Sub.prototype = Super.prototype; が駄目な理由 - mayokara note

以前書いたattach関数が何度もSuperClassって書かなきゃいけなくて微妙だったので手直し。

var attach = function(b,p){
    var F = function(){};
    F.prototype = b;
    var o = new F();
    for (var i in p) {
        var g = p.__lookupGetter__(i), s = p.__lookupSetter__(i);
        if (g || s) {
            if (g) o.__defineGetter__(i, g);
            if (s) o.__defineSetter__(i, s);
        } else {
            o[i] = p[i];
        }
    }
    return o;
};

var from = function(b,c,p){
    c.prototype = attach(b.prototype, p);
    c.prototype.__super__ = b;
    return c;
};
var Animal = function(){
    console.log("Animal: called");
};
Animal.prototype = {
    bark: function(){
        console.log("Animal#bark: ...");
    },
};

var Dog = from(Animal, function(){
    this.__super__.apply(this, arguments);
    console.log("Dog: called");
}, {
    bark: function(){
        console.log("Dog#bark: bowwow");
    },
});
var animal = new Animal();
// -> Animal: called
animal.bark();
// -> Animal#bark: ...

var dog = new Dog();
// -> Animal: called
// -> Dog: called
dog.bark();
// -> Dog#bark: bowwow
dog instanceof Dog // -> true
dog instanceof Animal // -> true
animal instanceof Dog // -> false
dog.__proto__ === Dog.prototype // -> true
dog.__proto__.__proto__ === Animal.prototype // -> true
dog.__proto__.__proto__.__proto__ === Object.prototype // -> true
dog.__proto__.__proto__.__proto__.__proto__ === null // -> true

単に引数増やしてthis.__super__を用意しただけ。

まあ、this.__super__潰さなくてもAnimalって直接呼べばいいんだけど、毎回書き方変わるの気持ち悪いな、ってことで。

this.__super__.prototype.barkで親クラスのbarkが呼べたりとか、そんな感じ。

追記

致命的な欠陥があったので修正。

JavaScript: application/xhtml+xmlなページを扱う際の注意

application/xhtml+xmlなページでcontinue_reading.user.jsが動かない問題が解決した。

既に更新済。以下覚書。

Range#createContextualFragmentでillegal string例外が起こる

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

この2行のせい。

つまり、<\?\w+とか<!\w+でこけている。

<--(HTMLにおけるコメント)は通る。

というわけで

var html = str.replace(/^[\s\S]*<html[^>]*>|<\/html>[\s\S]*$/ig, "");

解決。

innerHTMLに代入しようとすると例外が起こる

innerHTML、読み取り専用っぽい。

というわけで、DOM関数で組み立ててappendChildして解決。

余録:document.implementation.createDocument

http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490

var doc = document.implementation.createDocument("", "html", null);

でOK。

第2引数はdocumentElementを参照するために必要。

余録:Range#createContextualFragment

html/body要素が消える。

Firefox2ではhead要素も消え、事前にrange.setStartAfter(document.body)などとして何らかの選択をしないと評価されない。

「continue_reading.user.js」更新 その2

Greasemonkey: continue_reading.user.js - mayokara note

  • loadedURLsを保持するようにした
    • from AutoPagerize
    • 3→2→3→2→……みたいなパターンを回避できる
  • Google Images用のDocumentFilterを添付した
    • ソースの冒頭部でON/OFF可能(デフォルトはON)
    • DocumentFilterはnextLinkを引数に取る
  • パース時の処理をtry catchで括った

Google Imagesはウィンドウサイズに応じてカラム数を変えてくる

iframe.style.width = document.documentElement.clientWidth + "px"で対処。

overflow: hidden; width: 1px; height: 1px;なdiv要素で隠す。

document.adoptNode(node)とdocument.importNode(node, true)

外部ドキュメントからのノードは、現在のドキュメントに挿入する前に importNode() を使ってクローンを作る (あるいは adoptNode() を使って取り込む) べきです。

DOM:document.adoptNode - MDC

エラーは出さないけど、使うべきだそうな。

  • document.adoptNode(node)
    • 他のドキュメントのnodedocumentの所属にする
    • 読み込み中のimg要素を移した後、元のドキュメントを閉じると、読み込みが完了しない
  • document.importNode(node, true)
    • 他のドキュメントのnodedocumentに複製する
    • 読み込み中のimg要素を移した後、元のドキュメントを閉じても、読み込み続ける

iframe.contentWindow.addEventListener("DOMContentLoaded", F, false)

iframe.style.display = "none"だと発生しない(Firefox2だけ?

iframe.style.visibility = "hidden"の場合は問題ない。

application/xhtml+xmlなページのときRange#createContextualFragmentするとillegal characterエラー

相変わらず、さっぱり意味がわからない。

GM_xmlhttpRequestでoverrideMimeTypeをtext/htmlにしてるのがまずいのかなあ。

JavaScript: add/removeEventListenerラッパー

var ELController = function(target, type, handler, useCapture){
    return {
        on: function(){
            target.addEventListener(type, handler, useCapture);
        },
        off: function(){
            target.removeEventListener(type, handler, useCapture);
        },
    };
};
var onclick = new ELController(window, "click", function(){
    alert(1);
}, false);

(click) // -> none

onclick.on();

(click) // -> alert(1)

onclick.off();

(click) // -> none

クロージャを使って直接オブジェクトを返す形にすると、this._typeなどとして値を保持する必要がなくなる。

Firefox上では、EventListenerという名前はDOM2 EventsのEventListenerインタフェースとして、既に使われているみたい。


複数の同一の EventListener が、同じ EventTarget に同じ引数で登録された場合、重複するインスタンスは反映されません。EventListener が 2 度呼び出されることはなく、重複するインスタンスは反映されないので、removeEventListener で手動で削除する必要はありません。

DOM:element.addEventListener - MDC

イベントターゲット上にある現在のどのイベントリスナーも指定していない引数付きの removeEventListener は、何の効果もありません。

DOM:element.removeEventListener - MDC

イベントリスナは関数(というより、EventListenerインタフェース)単位で管理されているようで、連続でonしても1度しか実行されず、onしていない状態でoffしても特にエラーにはならない。

なので、状態を記憶して実行を制御しなくてもOK。

コンストラクタ関数の返り値とnew

コンストラクタ関数は別のオブジェクトを返り値として返すことも可能で、その場合は返されたオブジェクトがnew式の値となる。thisの値であったオブジェクトは破棄される。

JavaScript 第5版 - 9章 クラスとコンストラクタとプロトタイプ(1) - (DxD)∞

newをハッタリに使える。結果は関数呼び出しのときと同じ。

インスタンス生成とオブジェクト返却の比較

  • インスタンス生成
    • __proto__prototypeを繋ぐだけなのでコストが小さい
    • 内部値がpublicになってしまう
  • オブジェクト返却
    • 毎回オブジェクトを作って返すのでわずかにコストがかかる
    • 内部値をprivateにできる

livedoor Readerのj/kをマウスで

この辺をクリックの図

livedoor Readerのこの辺を

  • 左クリックでj
  • 右クリックでk

できるのに気づいた。

正確にはj/kじゃなくて、s/aも含めた前後移動機能みたいだけど。

ちゃんと先読みもしてくれるし、良い。


最速インターフェース研究会 :: livedoor Readerの自動スクロール機能

左クリック→左クリック押しっぱなしで、クリック間隔に応じた自動スクロールもできるとのこと。

この機能自体は知ってたんだけど、j/kの代わりにできるってことまでは考えてなかった。

追記

ホイール回すとs/aになる。

Re: GreasemonkeyからNode定数やKeyEvent定数を使う場合の注意点

GreasemonkeyからNode定数やKeyEvent定数を使う場合の注意点 - 素人がプログラミングを勉強するブログ

グローバル空間以外でクロージャを作って、自前でインスタンスを用意する方法もあります(クロージャを用意しないとエラーになる)

// ==UserScript==
// @name           use some constant values with evalInSandbox
// @namespace      http://mayokara.info/
// @description    sample script
// @include        http://*
// ==/UserScript==

(function(){

const Node = document.createElement("div");
const KeyEvent = document.createEvent("KeyboardEvent");

alert(Node.TEXT_NODE);
// -> 3

alert(KeyEvent.DOM_VK_RETURN);
// -> 13

})();

varで宣言すればグローバル空間でもクロージャを作らなくてもいけるようですが、変更されると困る類のもの定数なのでおすすめしません。

追記

セキュリティ的に問題がありますが、一応以下の方法でも取得はできます。

危険なので使用すべきではありません。

alert(Node.wrappedJSObject.TEXT_NODE);
// -> 3

alert(unsafeWindow.KeyEvent.DOM_VK_RETURN);
// -> 13