2つの画像を切り替え、なおかつそれぞれ別URLへリンク

[新着] Webテンプレートを仮オープンしました



0   名前: タロウ : 2006/04/20 00:18
早速ですが、質問させてください。

ボタンAと画像Bがあります。
ボタンAにマウスオーバーすると画像Bは画像Cに切り替わり、
ボタンAからマウスアウトしても画像Cはそのまま保持します。

ボタンAと画像Cはそれぞれ別のリンク先を指定したいのですが、
上手くいきません。
ボタンAをロールオーバーし、画像Bを画像Cに変えるまでは出来たのですが
画像Cにリンクを設定する方法がわからず、、。

例としてはボタンは1つ、画像1つの切り替えですが
最終的にはボタンを6つにします。


1   名前: Chip : 2006/04/20 00:18
とりあえずボタン二つのサンプル。

<html>
<head>
<script type="text/javascript">
function init(){
document.getElementById('linka').style.display = 'none';
document.getElementById('linkb').style.display = 'none';

var o = document.getElementById('img0');
o.parentNode.currentActiveObject = o;
document.getElementById('link1').onmouseover=function(){imgChange('linka')};
document.getElementById('link2').onmouseover=function(){imgChange('linkb')};

}
function imgChange(oId){
var o = document.getElementById(oId);
o.parentNode.currentActiveObject.style.display = 'none';
o.style.display = '';
o.parentNode.currentActiveObject = o;
}
</script>
</head>
<body onload="init()">

<p>
<a href="http://www.google.co.jp/" id="link1">google</a>
<a href="http://www.yahoo.co.jp/" id="link2">yahoo</a>
</p>

<p>
<img src="" alt="画像" id="img0">
<a href="http://www.goo.ne.jp/" id="linka"><img src="" alt="goo" width="100" height="30"></a>
<a href="http://www.tagindex.com/" id="linkb"><img src="" alt="TagIndex" width="100" height="30"></a>
</p>

</body>
</html>

2   名前: タロウ : 2006/04/20 00:18
>>Chipさん

早速のお返事ありがとうございます。
ソースをコピペして使ってみましたところInternetExplorer等の表示は
正常なのですが、Netscape4.7では表示がおかしくなってしまいました。

Netscape4.7以上をサポートする形にはできないのでしょうか?
質問ばかりで恐縮ですが、よろしくお願いいたします。

3   名前: Chip : 2006/04/20 00:18
古いブラウザでの動作についてはスンマセンが私はパスです。
それよりはjavascript - off/無効 でもどうにかなるサイト作りをするべきだと思っているので。

function init(){
if(! document.getElementById) return; // ←1行追加 古いブラウザでエラーを出ないようにする処置。

4   名前: Zippsthreadbone : 2006/04/20 00:18
Where is Usability-Policy gone? Crap!! F**k off!
if(document.layers){}
and use Array() with links.

5   名前: Chip : 2006/04/20 00:18
>>3
何いってるかよくわからんのですが
リンクの数だけベタ書きにするのは手抜きだったのでちょっと変更。

<html>
<head>
<style type="text/css">
</style>
<script type="text/javascript">
function init(id1,id2){
if(! document.getElementById) return;
var o1 = document.getElementById(id1);
var anchors1 = o1.getElementsByTagName('a');
var o2 = document.getElementById(id2);
var anchors2 = o2.getElementsByTagName('a');
for(var i=0;i<anchors1.length;i++) {
anchors1[i].onmouseover = function(){imgChange(this)};
anchors1[i].targetObject = anchors2[i];
anchors2[i].style.display = 'none';
}
o1.currentActiveObject = o2.getElementsByTagName('img')[0];
}
function imgChange(o){
o.parentNode.currentActiveObject.style.display = 'none';
o.targetObject.style.display = '';
o.parentNode.currentActiveObject = o.targetObject;
}
</script>
</head>
<body onload="init('link1','link2')">

<p id="link1">
<a href="http://www.google.co.jp/">google</a>
<a href="http://www.yahoo.co.jp/">yahoo</a>
</p>

<p id="link2">
<img src="" alt="画像" id="img0">
<a href="http://www.goo.ne.jp/"><img src="" alt="goo" width="100" height="30"></a>
<a href="http://www.tagindex.com/"><img src="" alt="TagIndex" width="100" height="30"></a>
</p>

</body>
</html>

6   名前: Pid : 2006/04/20 00:18
>>2
> Netscape4.7以上をサポートする

Netscape 4.x 以下と 6.0 以上(Gecko)とでは全く別物ですが。6.0 以上なら動くはずです。


>>5
細かい点ですが,

> for(var i=0;i<anchors1.length;i++) {

NodeList である anchors1 は生きていますので,ループ回数だけ length にアクセスするのはいろんな意味で得策ではありません。length 値をループ前に別変数に入れた方が良いでしょう。
http://d.hatena.ne.jp/jintrick/20050322
http://nanto.asablo.jp/blog/2006/02/21/262269

> o.targetObject.style.display = '';

CSS の文法上,display 値が空文字列になることはありません。CSS2 では,不正な値の宣言は無視しなければならないと定められていますので,多くのブラウザではエラー処理として初期値に戻すようです。しかし,DOM2-CSS では具体的なエラー処理を明記していないため,「初期値に戻す」のは実装依存の可能性が高いと思われます。

> anchors1[i].targetObject = anchors2[i];

個人的には,(やむをえない場合を除き)組み込みのインタフェースを拡張するのは避けるべきと思います。と言うのは,基本的に JavaScript はプロパティの属性設定ができないので,複数のプログラムを組み合わせた場合に不測の事態が生じやすいからです。

上の方法以外では,Array でペアを保存しておくのがおそらく簡単でしょう。また,document.links(anchors),document.images および name 属性を使えば Netscape 4.x に対応できます(というのが >>4 の御主旨でしょうか?ただ,今回の件で document.layers を使う余地はありません)。

※もっとも,画像切替のサポートが必ずしもユーザビリティ向上にはつながらないという点で,「あえて動かさない」という >>3 の Chips さんのスタンスに私は賛成です。

7   名前: Chip : 2006/04/20 00:18
>>6
色々ありがとうざいます。
いくつかご教授ください。

>> for(var i=0;i<anchors1.length;i++) {
> NodeList である anchors1 は生きています(中略)length 値をループ前に別変数に入れた方が良い
この場合にlengthを別にとる事の意味ってあるのでしょうか。
生きているが故に危険であるというのであれば本質的な危険はlengthの変化よりもノード自体の変化であって、
lengthを別変数に入れるだけでは片手落ちでノードのコピーの配列を用意しなければいけないのではないでしょうか?

もう一つの組み込みインターフェースの拡張の件もそうですが、全体を把握しきれていなければDOMを利用したアクセスは全てが危険という事がいえるし、
そうした事まで配慮するならDOMツリー全体のコピーでも確保した上でなければサンプルコードは書けなくってしまうけど、そこまですると冗長な気もするし…
(というか、どう変化するかわからないという事を前提にしてしまうとコピーとったとしても徒労に終わる可能性もありますね)
そんなわけで、とりあえず他プログラムの存在は無視していますがやっぱまずいでしょうか?
勝手に追加する時のプロパティ名なんかはもうちょっと考えないとイカンな、と、反省もしましたが。


>> o.targetObject.style.display = '';
>実装依存の可能性が高いと
これは何にも考えていませんでした。
DOM Inspectorなんかで見る限り(未設定時点では)''に見えるんで''に戻したつもりだったんですが
どう対処すべきなんでしょうか?
ノードのstyleオブジェクトがインラインスタイル相当なら'inline'で上書きするのも違うような気もするし。

>>document.layers を使う余地はありません
当人ではないので想像の域を出ませんが、layesの有無で分岐してなんとかせぇって意味だと捉えてました。

8   名前: Chip : 2006/04/20 00:18
>> o.targetObject.style.display = '';
これは
o.targetObject.style.removeProperty('display');
と、すべきでしたね。

9   名前: Pid : 2006/04/20 00:18
>>7-8

> for(var i=0;i<anchors1.length;i++)
> この場合にlengthを別にとる事の意味ってあるのでしょうか。

主な理由としては,

(1). ループの繰り返しのたびに anchor1 を再検索するので非効率(>>6 のリンク先に実測値があります)。
(2). anchors1.length の値が一定とは限らない(通常はループ中に別処理が割り込むことはほとんどありませんが,スクリプトの組み合わせによってはありうる)。


> 生きているが故に危険であるというのであれば本質的な危険はlengthの変化よりもノード自体の変化であって、lengthを別変数に入れるだけでは片手落ちでノードのコピーの配列を用意しなければいけないのではないでしょうか?

仰る通り,場合によっては NodeList を Array にした方が良いです。ただ,その場合も for/while で各要素をコピーすることになりますので,結局は同じ問題(NodeList.length のスナップショットをとるかとらないか)に直面します。そして,上述の理由から,私はスナップショットをとった方が良いと思います。
※ JavaScript 1.6 なら Array.map を使えますが。


> どう変化するかわからないという事を前提にしてしまうとコピーとったとしても徒労に終わる可能性もありますね

それに対しては,DOM2-Traversal&Range が部分的な(?)解決になると思います。


> もう一つの組み込みインターフェースの拡張の件もそうですが……中略……とりあえず他プログラムの存在は無視していますがやっぱまずいでしょうか?

>>6 は言葉足らずでした(すみません)ので補足しますと,JavaScript には拡張したプロパティを隠すための直接の手段がありません。たとえば,プロパティに対して ReadOnly や DontEnum 属性をユーザ指定できませんし、private 空間に閉じ込めることもできません。したがって,拡張したプロパティは常に外部に公開される形になりますので,参照されて(破壊されて)困るような値を保存する手段として好ましくないと考えます。
※ JavaScript 2.0 なら隠すことが可能ですが。

まあ,そこまで考える必要があるか?と私も思います (^^;)。しかし現実に,利用者(特に,いろんなスクリプトをぺたぺた貼る入門者の方々)は製作者の思いもよらない使い方をしますから,できるだけ他のスクリプトに干渉しない・されないように作っておいて損は無いと思います。


> ノードのstyleオブジェクトがインラインスタイル相当なら'inline'で上書きするのも違うような気もするし。

それで良いと私は思います(厳密に考えるなら getComputedStyle/currentStyle で値を保存した方が良いかも)。removeProperty も面白いですね。私が気になったのは,たいていのブラウザでは,

o.style.display = null;
o.style.display = 'ありえない値';
o.style.display = 100;

などと書いても初期値に戻るということです。空文字列の代入というのが仕様的に保証された動作なのか,それともこれらの例と同じ例外処理に過ぎないのか,実は私も自信がありません (^^;)。ご存知でしたらぜひご教授下さい。

10   名前: Pid : 2006/04/20 00:18
>>6 で Array を使うと書きましたが,クロージャで大丈夫ですね(汚くて読みづらいのはご勘弁 m(_ _)m)。

// Safari,Opera,Firefox テスト用
if (document.implementation &&
document.implementation.hasFeature ('HTML', '2.0') &&
document.implementation.hasFeature ('CSS', '2.0') &&
document.implementation.hasFeature ('Events', '2.0'))

/*document.*/ addEventListener ('load', function () {
////////////////////////////////////////////////////////////

// init 関数のスコープは無名関数内なので,外部から参照されない
function init (id1, id2) {
var o1 = document.getElementById (id1);
var o2 = document.getElementById (id2);
var anc1 = o1 && o1.getElementsByTagName ('a');
var anc2 = o2 && o2.getElementsByTagName ('a');

if (o1 && o2)
for (var currentActiveObject = o2.getElementsByTagName ('img')[0], view = document.defaultView,
I = anc1.length, i = 0; i < I; anc2[i++].style.display = 'none')

// anc1[n] と anc2[n](targetObject)を結び付けるクロージャ(たぶんメモリリークしないと思いますが……)
(function (targetObject, displayValue) {
anc1[i].addEventListener ('mouseover', function () {
currentActiveObject.style.display = 'none';
currentActiveObject = targetObject;
currentActiveObject.style.display = displayValue;
}, false);
} )(anc2[i], view.getComputedStyle (anc2[i], null).getPropertyValue ('display'));
}

// 設定
init ('link1', 'link2');

////////////////////////////////////////////////////////////
}, false);

11   名前: タロウ : 2006/04/20 00:18
皆さんお返事ありがとうございました。
頂いたお返事を参考に、自分なりのアレンジを加えて形にすることができました。

ありがとうございました。

12   名前: Chip : 2006/04/20 00:18
>>9 リストのlengthの件。
ちょっと勘違いがあったようです。
(2)の理由がある以上おっしゃるとおりで退避すべきでした。
(1)は微妙・・・リンク先の実測値は実用値としては問題のある数値ではないと感じました。(10000って・・・)
この速度差とソースの可読性を天秤にかけるだけなら、 anc1.lengthを別変数にするのはやり過ぎかと。


拡張したプロパティの件。
実際に閉じ込めることができている >>10 はちょっとビックリしました。
「クロージャ」っていうのが理解できていないけどこれから勉強させてもらいます。


style.display = 空文字 の件。
DOMのエラー規定云々の前に、CSSのパーサとして正当な動作をするなら初期値にするのではなく代入文そのものを無視すべきな気もしますが・・・謎ですね。
それはともかく、>>10のように現状のスタイルを保持しておいて復元すれば問題ないですね。
='inline'が違うと云ったのは スタイルで a{display:block} みたいな事をされていたらおかしな事になると思ったからでした。
だけどインラインで<a style="display:block;">にされていたら空文字代入やremovePropertyでは復元できていませんでした。

13   名前: Pid : 2006/04/20 00:18
※脱線すみません。

> DOMのエラー規定云々の前に、CSSのパーサとして正当な動作をするなら初期値にするのではなく代入文そのものを無視すべきな気もしますが

全く仰る通りです。私もそこがよく分からないのです (^^;)。


> 拡張したプロパティの件。

後で思い出したのですが,DOM3-Core では Node.setUserData/Node.getUserData で各ノードにデータを持たせることができます。それを考えると,拡張プロパティであることさえ分かればうるさく言う必要はないのかな……と考え直してみたり。

一覧へ戻る