素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

CSSセレクタをXPathに変換して要素を取得するgetElementsBySelectorを作った

つかれた。

(function(){
 var regex=/(\w+|\*)?(?:([.#])(\w+))?(?:\[(\w+)((?:[=~^$*|])?=["'](\w+)["'])?\])?(?:(?::([\w-]+)(?:\((.*?)\))?)?)?([ >+~,])?\s*(.*)/;
var xpath_snippet={
'.':'contains(concat(" ",normalize-space(@class)," "), " $CODE ")',
'#':'@id="$CODE"',
'=':'$ATTR=$CODE',
'~':'contains(concat(" ",@$ATTR," "), " $ATTR_VALUE " )',
'^':'starts-with(@$ATTR, "$ATTR_VALUE" )',
'$':'substring(@$ATTR, string-length(@$ATTR) - string-length("$ATTR_VALUE") + 1) = "$ATTR_VALUE"',
'|':'@$ATTR="$ATTR_VALUE" or starts-with(@ATTR, "$ATTR_VALUE-")',
'first-child':'.//*[position()=1]',
'last-child':'.//*[position()=last()]',
'nth-child':'.//*[count(preceding-sibling::*)=$N-1]',
'nth-last-child':'.//[count(following-sibling::*)=n-1]',
'nth-of-type':'.//*[position() = $N]',
'nth-last-of-type':'.//*[last() - $N + 1]',
'only-child':'.//*[last() = 1 and ./]',
'only-of-type':'.//*[last() = 1]',
'>':'/',
' ':'//',
'+':'/following-sibling::*[1]/',
'~':'/following-sibling::',
',':'|'
};


getElementsBySelector=function(selector,context){
	var xpath='.//'+(function(selector,flag){

		var [,tag,class_or_id,class_id_name,attr_name,attr_c,attr_value,p_name,p_attr,split_c,remain]=regex(selector);

	var xpath_base=[];
	var attr_base=[];
	
	var tag=flag?'':tag||'*';
	
	xpath_base.push(tag);

	if(class_or_id){
		attr_base.push(xpath_snippet[class_or_id].replace('$CODE',class_id_name));
	if(attr_name){
		if(attr_c){
			attr_base.push(xpath_snippet[attr_c].replace('$ATTR',attr_name).replace('$ATTR_VALUE',attr_value));
		}
		else{
			attr_base.push('@'+attr_name);
		}
		}
	}

	if(p_name){
		if(p_name=='not'){
			attr_base.push('not('+arguments.callee(p_attr,true)+')');
		}else{
		attr_base.push(xpath_snippet[p_name].replace('$N',p_attr=='even'?'mod 2=1':p_attr=='odd'?'mod 2=0':p_attr.replace(/(\d+)\s*n/g,'mod $1=1')));
		}
		}
	if(attr_base.length){
	xpath_base.push('['+attr_base.join(' and ')+']');
	}

	if(split_c){
	xpath_base.push(xpath_snippet[split_c]+arguments.callee(remain));
	}
	return xpath_base.join('');
})(selector)

var xpath_result=document.evaluate(xpath,context||document,null,7,null);

var ret=[];

for(var i=0;i<xpath_result.snapshotLength;i++)ret.push(xpath_result.snapshotItem(i));
return ret;
}
})()
getElementsBySelector('#days .day a[href]')

やけに複雑。速度は出ない。

やっぱりSelectorsAPI欲しいよ。