jQuery Logo jQuery を使ったサンプル

作成日:2015年9月25日

ローディング画面を表示

簡単にページ読み込み中のローディング画面を表示する方法です。ローディング画面に表示するアニメーションは CSS アニメーションを使用しますが、以下のようなサイトにローディングアニメーションが掲載されているのでそれを利用します。

CSS3 Loading Animation などで検索すると色々と見つかります。

以下は HTML と jQuery の読み込みとスクリプト部分(後述)です。

body 要素の先頭にローディング画面の div 要素(クラス page-loader)を配置して、その中にアニメーションの領域の div 要素(クラス spinner)を配置します。

その後に、通常表示するコンテンツを配置します。この例では、大き目の画像を3つ配置しているだけです。

<body>
<div class="page-loader">
  <div class="spinner"></div>
</div>

<div>
  <p><img src="../images/largeImage01.jpg" alt=""></p>
  <p><img src="../images/largeImage02.jpg" alt=""></p>
  <p><img src="../images/largeImage03.jpg" alt=""></p>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
<script>
// ページのロード完了時にローディング画面をフェードアウト
$(window).on('load', function() {
  $(".page-loader").fadeOut('fast');
});
</script>
</body>

以下は CSS です。

ローディング画面の div 要素(クラス page-loader)は、画面いっぱいに表示するため、position: fixed; top: 0; left: 0; width: 100%; height: 100%; を指定します。 z-index: 10001; は最前面に表示するための指定です。他の要素より大きい値が指定されていれば問題ありません。

background: #f2f2f2; はローディング画面の背景色です。適宜変更します。

11行目から37行目はローディング画面に表示する CSS3 アニメーションの記述です。この部分は前述のサイトからコピーしています。

.page-loader {
  position: fixed;
  z-index: 10001;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #f2f2f2; //ローディング画面の背景色
}

.spinner {
  width: 40px;
  height: 40px;
  margin: 200px auto;
  background-color: #F24245; //アニメーション画像の色
  border-radius: 100%;  
  -webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
  animation: sk-scaleout 1.0s infinite ease-in-out;
}
//アニメーション
@-webkit-keyframes sk-scaleout {
  0% { -webkit-transform: scale(0) }
  100% {
    -webkit-transform: scale(1.0);
    opacity: 0;
  }
}
@keyframes sk-scaleout {
  0% { 
    -webkit-transform: scale(0);
    transform: scale(0);
  } 100% {
    -webkit-transform: scale(1.0);
    transform: scale(1.0);
    opacity: 0;
  }
}

以下は Spinkit の例です。気に入ったローディングアニメーションがあれば、画面上の「Source」をクリックすると、その HTML と CSS が表示されるので、それをコピーします。

Spinkitのアニメーションのスクリーンショット

アニメーションの領域のクラス名などは、コピーした内容に合わせて適宜変更します。

以下は jQuery の読み込みとスクリプト部分です。

全ての画像やコンテンツが読み込まれた時点で処理を行うので window の load イベントを使用します。

以下では、単にページのロード(読み込み)が完了した時点で、ローディング画面(クラス page-loader の div 要素)を fadeOut() を使ってフェードアウトさせているだけです。引数にミリ秒の数値を指定してスピードを調整することもできます。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
<script>
// ページのロード完了時にローディング画面をフェードアウト
$(window).on('load', function() {
  $(".page-loader").fadeOut('fast');
});

/* または、window.onload を利用して
window.onload = function() {
  $(".page-loader").fadeOut('fast');
}*/
</script>
</body>

以下は、ローディング画面のフェードアウトのタイミングを調整する例です。3行目では delay(500) を追加してフェードアウトを 0.5秒遅らせています。7行目からは、setTimeout() を使って、万が一ページのロードが完了しない場合でも、強制的にローディング画面をフェードアウトして非表示にしています。

// ページのロード完了時にローディング画面をフェードアウト
$(window).on('load', function() {
  $(".page-loader").delay(500).fadeOut(600);
});
  
// ページのロードが完了しない場合でも10秒たったら強制的にローディング画面をフェードアウト
setTimeout('stoploading()', 10000); 
function stoploading() {
  $(".page-loader").fadeOut('fast');
}

以下はサンプルです。各ページの最後に HTML、CSS、jQuery を記載してあります。以下のサンプルはローディング画面をある程度の時間表示するため delay() で調整しています。

リンクをクリックすると Google マップを表示

HTML 上に記述してある場所のリストの「地図を表示」と言うリンクをクリックすると、その場所の地図を表示する例です。

Google Map JavaScript API の基本的な使い方に関しては「Google Maps API の使い方・利用方法」をご覧ください。

以下は HTML と CSS です。

HTML には、地図を生成する際に必要な「緯度(lat)」「経度(lng)」「ズームレベル(zoom)」の値を記述しておき、CSS(display:none)で非表示にします。緯度と経度は必須ですが、ズームレベルは指定がなければデフォルトで 16 を適用するようにしています。

HTML
<body>
<!--緯度(lat)と経度(lng)は https://google-developers.appspot.com/maps/documentation/utils/geocoder/?hl=ja で住所を入力して確認します-->
<div class="map_info">
  <p class="venue">東京スカイツリー</p>
  <p class="address">〒131-0045 東京都墨田区押上1−1−2</p>
  <p class="lat">35.710033</p>
  <p class="lng">139.810716</p>
  <p class="web"><a href="http://www.tokyo-skytree.jp/" target="_blank">www.tokyo-skytree.jp</a></p>
  <p class="zoom">15</p>
  <p class="show_map"><a href="#">地図を表示</a></p>
</div>
<div class="map_info">
  <p class="venue">東京タワー</p>
  <p class="address">〒105-0011 東京都港区芝公園4−2−8</p>
  <p class="lat">35.658593</p>
  <p class="lng">139.745441</p>
  <p class="web"><a href="http://www.tokyotower.co.jp/index.html" target="_blank">www.tokyotower.co.jp</a></p>
  <p class="zoom">16</p>
  <p class="show_map"><a href="#">地図を表示</a></p>
</div>
<div class="map_info">
  <p class="venue">東京駅</p>
  <p class="address">東京都千代田区丸の内1丁目</p>
  <p class="lat">35.681167</p>
  <p class="lng">139.767052</p>
  <p class="web"><a href="http://www.jreast.co.jp/estation/station/info.aspx?StationCd=1039" target="_blank">www.jreast.co.jp</a></p>
  <p class="zoom">18</p>
  <p class="show_map"><a href="#">地図を表示</a></p>
</div>
<div id="map_container">
  <div id="map_canvas"></div>
</div>
CSS
/* 個々の場所の情報を記述してある div 要素 */
.map_info {
  margin-bottom: 50px;
}
.lat, .lng, .zoom {   /* 緯度・経度、ズームレベルを記載した要素(非表示) */
  display: none;
}
#map_container {   /* 地図のコンテナ */
  clear: both;
  width: 90%;
  padding: 4px;
  border: 1px solid #CCC;
  display: none;   /* 最初は非表示 */
}
#map_canvas {   /* 地図を表示する要素 幅と高さを指定 */
  width: 100%;
  height: 400px;
  display: none;   /* 最初は非表示 */
}
.map_info p.web {
  display: none;  /* 情報ウィンドウにのみ表示 */
}
/* 情報ウィンドウ(マーカーをクリックすると表示される領域) */
#map_content {
  width: 250px;
  height: 70px;
}

初期状態では以下のような表示になります。

初期画面のスクリーンショット

「地図を表示」と言うリンクをクリックすると、その下に地図が表示されます。

リンクをクリックして地図が表示された画面のスクリーンショット

以下が jQuery の部分です。

jQuery と Google Maps JavaScript API を読み込みます。 Google Maps JavaScript API の読み込みでは「&callback=initMap」でのコールバック関数は指定しません。また「YOUR_API_KEY」の部分は取得した API キーで置き換えます。

リンクをクリックしたら地図を表示する showMap() と言う関数を作成して表示します。この関数の引数は以下の通りです。

  • address:場所の住所(情報ウィンドウで表示する際に使用します)
  • lat:緯度(地図を生成する際に使用します)
  • lng:経度(地図を生成する際に使用します)
  • title:マーカーに表示するタイトルです
  • url:情報ウィンドウに場所の名前を表示する際に、URL が記述されていればリンクとして表示します
  • zoom:ズームレベル

7行目~58行目が関数の定義部分です。60行目~91行目までがクリックイベントの設定です。

90行目の「return false」はリンクのデフォルトの動作を無効にする(クリックするとリンク先へ移動するのを防ぐ)ための記述です。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
<!-- 順番に読み込まれる必要があるので「async defer」は指定しない。YOUR_API_KEYの部分は取得した APIキーで置き換えます。 --> 
<script>
jQuery(function($){
  var map, map_center;
  function showMap(_address, _lat, _lng, _title, _url, _zoom) {
    //表示領域は CSS で非表示にしてあるので表示に
    $("#map_canvas").css('display', 'block');
    
    //地図の中心位置(引数 _lat, _lng から生成)
    map_center = new google.maps.LatLng(_lat, _lng);
    
    //マップ生成のオプション
    var opts = {
      //地図の中心
      center: map_center,
      //初期のズーム レベル
      zoom: _zoom ,
      //初期マップ タイプ  
      mapTypeId: "roadmap"
    };
    
    //マップのインスタンスを生成
    map = new google.maps.Map(document.getElementById("map_canvas"), opts);
    
    //マーカーのインスタンスを生成
    var marker = new google.maps.Marker({
      //マーカーを配置する Map オブジェクトを指定
      map: map,
      //マーカーの初期の場所を示す LatLng を指定  
      position: map_center,  
      //マーカーをアニメーションで表示
      animation: google.maps.Animation.DROP,  
      title:_title
    });

    //情報ウィンドウに表示するコンテンツを作成
    //_urlが指定してあればリンクつきのタイトルを表示(URLがない場合もあるため)
    var _content;
    if (_url) {
      _content = '<div id="map_content"><p><a href="' 
      + _url + '" target="_blank"> ' + _title + '</a><br />' 
      + _address + '</p></div>';
    }else {
      //_urlが指定してなければ、リンクなしのタイトルと住所を表示
      _content = '<div id="map_content"><p>' 
      + _title + '<br />' 
      + _address + '</p></div>';
    }

    //情報ウィンドウのインスタンスを生成
    var infowindow = new google.maps.InfoWindow({
      content: _content,
    });

    //marker をクリックすると情報ウィンドウを表示(リスナーの登録)
    marker.addListener('click', function() {
      //第2引数にマーカーを指定して紐付け
      infowindow.open(map, marker);
    }); 
  }

  $('p.show_map a').click(function() {
    //closeクラスが付与されていれば、地図が表示されているので非表示に
    if($(this).hasClass('close')) {
      $('#map_canvas').css('display', 'none');
      $('#map_container').css('border-width', 0).fadeOut(500);
      $('p.show_map a').removeClass('close').text('地図を表示');
    }else{
      //リンクの文字「地図を表示」を「地図を閉じる」に変更
      $(this).addClass('close').text('地図を閉じる');
      //それ以外のリンクは「地図を表示」に(別の地図が閉じられていない場合があるため)
      $('p.show_map a').not(this).removeClass('close').text('地図を表示');
      //map_infoクラスの div 要素のインスタンスを変数に代入
      var map_info$ = $(this).closest('.map_info');
      //緯度・経度の取得
      var lat =parseFloat( map_info$.find(".lat").text());
      var lng =parseFloat( map_info$.find(".lng").text());
      //map_infoクラスの div 要素に地図を表示する領域を移動 append
      map_info$.append($('#map_container'));
      var address = map_info$.find('p.address').text();
      //console.log(address);
      if(map_info$.find('p.web').text() != '') {
        var url = map_info$.find('p.web a').attr('href');
      }
      //地図を表示する領域に枠線を付けてフェードインで表示
      $('#map_container').css('border-width', '1px').fadeIn(1000);
      //zoom を取得
      var zoom = map_info$.find(".zoom").text() ?  parseInt(map_info$.find(".zoom").text()): 16;
      //マーカーのタイトル
      var title = map_info$.find('p.venue').text();
      //地図を表示
      showMap(address, lat, lng, title, url, zoom);
    }
    return false;
  });
}); 
</script> 
</body>

Geocoding API(ジオコーディング)を使う場合

ジオコーディングを使うと住所から緯度・経度を取得できます。以下は前述の例をジオコーディングを使って書き換えたものです。

但し、ジオコーディングを使うと処理が重くなります。API も Map JavaScript API と Geocoding API の2つを利用することになります。Geocoding API に何らかの問題があると、位置を取得できない可能性があるので、前述の例の方が確実な方法と言えます。こちらの利点は場所の情報を記載する際に、緯度・経度を取得して記述する必要がないという点です。

以下が HTML と CSS です。緯度・経度を記述する要素がない以外は、前述の例とほぼ同じです。

HTML
<div class="map_info">
  <p class="venue">東京スカイツリー</p>
  <p class="address">〒131-0045 東京都墨田区押上1−1−2</p>
  <p class="web"><a href="http://www.tokyo-skytree.jp/" target="_blank">www.tokyo-skytree.jp</a></p>
  <p class="zoom">15</p>
  <p class="show_map"><a href="#">地図を表示</a></p>
</div>
<div class="map_info">
  <p class="venue">東京タワー</p>
  <p class="address">〒105-0011 東京都港区芝公園4−2−8</p>
  <p class="web"><a href="http://www.tokyotower.co.jp/index.html" target="_blank">www.tokyotower.co.jp</a></p>
  <p class="zoom">17</p>
  <p class="show_map"><a href="#">地図を表示</a></p>
</div>
<div class="map_info">
  <p class="venue">東京駅</p>
  <p class="address">東京都千代田区丸の内1丁目</p>
  <p class="web"></p>
  <p class="zoom"></p>
  <p class="show_map"><a href="#">地図を表示</a></p>
</div>
<div id="map_container">
  <div id="map_canvas"></div>
</div>
CSS
#map_container {
  clear: both;
  width: 90%;
  padding: 4px;
  border: 1px solid #CCC;
  display: none;   /* 最初は非表示 */
}
#map_canvas {   /* 高さ(と幅)を指定しないと地図は表示されない */
  width: 100%;
  height: 400px;
  display: none;   /* 最初は非表示 */
}
.map_info p.web {
  display: none;  /* 情報ウィンドウにのみ表示 */
}
.zoom {
  display: none;   /* 非表示 */
}
/* 情報ウィンドウ(マーカーをクリックすると表示される領域) */
#map_content {
  width: 250px;
  height: 70px;
}

以下が jQuery の部分です。

7行目~68行目が、地図を表示する関数の定義です。70行目~103行目がクリックイベントの設定です。ジオコーディングを使っている以外は、前述の例とほぼ同じです。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
<!-- 順番に読み込まれる必要があるので「async defer」は指定しない。YOUR_API_KEYの部分は取得した APIキーで置き換えます。 --> 
<script>
jQuery(function($){
  var map, map_center;
  function showMap(_address, _title, _url, _zoom) {
    //表示領域は CSS で非表示にしてあるので表示に
    $("#map_canvas").css('display', 'block');
    
    //マップ生成のオプション
    //center は Geolocation から取得して後で設定
    var opts = {
      //初期のズーム レベル
      zoom: _zoom,
      //初期マップ タイプ  
      mapTypeId: "roadmap"
    };
    
    //マップのインスタンスを生成
    map = new google.maps.Map(document.getElementById("map_canvas"), opts);
    
    //ジオコーディングのインスタンスの生成
    var geocoder = new google.maps.Geocoder();

    //geocoder.geocode() にアドレスを渡して、コールバック関数を記述して処理
    geocoder.geocode( { 'address': _address}, function(results, status) {
      if (status === 'OK' && results[0]) {
        //results[0].geometry.location に緯度・経度のオブジェクトが入っている
        map_center = results[0].geometry.location;
        //地図の中心位置を設定
        map.setCenter(map_center);
        //マーカーのインスタンスを生成
        var marker = new google.maps.Marker({
          //マーカーを配置する Map オブジェクトを指定
          map: map,
          //マーカーの初期の場所を示す LatLng を指定  
          position: map_center,  
          //マーカーをアニメーションで表示
          animation: google.maps.Animation.DROP,  
          title:_title
        });

        //情報ウィンドウに表示するコンテンツを作成
        //_urlが指定してあればリンクつきのタイトルと住所を表示(URLがない場合もあるため)
        var _content;
        if (_url) {
          _content = '<div id="map_content"><p><a href="' + _url 
          + '" target="_blank"> ' + _title + '</a><br />' 
          + _address + '</p></div>';
        }else {
          //_urlが指定してなければ、リンクなしのタイトルと住所を表示
          _content = '<div id="map_content"><p>' + _title + '<br />' 
          + _address + '</p></div>';
        }
        
        //情報ウィンドウのインスタンスを生成
        var infowindow = new google.maps.InfoWindow({
          content: _content,
        });
 
        //marker をクリックすると情報ウィンドウを表示(リスナーの登録)
        google.maps.event.addListener(marker, 'click', function() {
          //第2引数にマーカーを指定して紐付け
          infowindow.open(map, marker);
        });
      } else {
        alert("住所から位置の取得ができませんでした。: " + status);
      }
    });    
  }

  $('p.show_map a').click(function() {
    //closeクラスが付与されていれば、地図が表示されているので非表示に
    if($(this).hasClass('close')) {
      $('#map_canvas').css('display', 'none');
      $('#map_container').css('border-width', 0).fadeOut(500);
      $('p.show_map a').removeClass('close').text('地図を表示');
    }else{
      //リンクの文字「地図を表示」を「地図を閉じる」に変更
      $(this).addClass('close').text('地図を閉じる');
      //それ以外のリンクは「地図を表示」に(別の地図が閉じられていない場合があるため)
      $('p.show_map a').not(this).removeClass('close').text('地図を表示');
      //map_infoクラスの div 要素のインスタンスを変数に代入
      var map_info$ = $(this).closest('.map_info');
      //map_infoクラスの div 要素に地図を表示する領域を移動 append
      map_info$.append($('#map_container'));
      var address = map_info$.find('p.address').text();
      var my_reg = /〒\s?\d{3}(-|ー)\d{4}/;
      //郵便番号を含めるとおかしくなる場合があったので、郵便番号は削除
      address = address.replace(my_reg, '');
      //URL を取得
      if(map_info$.find('p.web').text() != '') {
        var url = map_info$.find('p.web a').attr('href');
      }
      //地図を表示する領域に枠線を付けてフェードインで表示
      $('#map_container').css('border-width', '1px').fadeIn(1000);
      //zoom を取得
      var zoom = map_info$.find(".zoom").text() ?  parseInt(map_info$.find(".zoom").text()): 16;
      //マーカーのタイトル
      var title = map_info$.find('p.venue').text();
      //地図を表示
      showMap(address, title, url, zoom);
    }
    return false;
  });
}); 
</script> 

スパム対策

ウェブ上にメールアドレスを掲載すると、スパム(迷惑)メールが届くようになることがあります。以下はメールアドレス収集プログラムに収集されるのを防止する方法の1つの例です。

メールアドレス用のリンク

メールアドレスをウェブ上に掲載する場合、a 要素を使ってリンク先として href 属性値に「mailto:」から始まる文字列を記述してメールアドレス用のリンクを作ることができます。

<a href="mailto:info@example.com">info@example.com</a>

info@example.com

表題(subject)付きのアドレスの場合は ?subject= を追加して以下のように記述します。

<a href="mailto:info@example.com?subject=お問い合わせ">info@example.com</a>

info@example.com

更に本文(body)も追加するには、以下のように記述します。

<a href="mailto:info@example.com?subject=件名&amp;body=本文">info@example.com</a>

info@example.com

以下のような書式になっています。

mailto:メールアドレス?subject=件名&amp;body=本文
  • ? … メールアドレスとその後の文字列を ? で区切ります
  • subject= … メールの件名を記述します
  • body= … メールの本文を記述します
  • &amp; … 件名と本文を & マークで区切ります(文字参照の &amp; を使用します)

また、表示する文字をメールアドレス以外にする場合は以下のように記述できます。

<a href="mailto:info@example.com?subject=お問い合わせ">カスタマーサービス</a>

カスタマーサービス

しかし、上記の方法でメールアドレスを公開すると、HTML ソース中にメールアドレス(xxxx@xxx.xxx)を掲載することになり、また a 要素の href 属性に「mailto:」を記述していることからメールアドレスであることがわかってしまい、メールアドレス収集プログラムに簡単に収集されてしまいます。

JavaScriptでメールリンクを生成

JavaScript(jQuery)を使ってメールリンクを生成すると、HTML ソースや JavaScript ソースに直接メールアドレスは含まれないので、(自動収集プログラムが、単にファイルの中身を読むだけであれば)自動収集をある程度防ぐことができます。

以下は、件名や本文を含まない単純なメールリンクを生成する例です。

まず、HTML への記述の方法を決めておく必要がるので、以下のようなルールにします。

  • メールアドレスを表示する部分は span 要素で記述し、クラスを「nsm」(no spam mail の略)とすることとします(任意の値でかまいません)。
  • メールアドレスの「@」を半角英字のカンマ「,」に換えて記述します。
<!-- メールアドレスの記述 -->
<span class="nsm">info,example.com</span>

info,example.com

以下は、JavaScript(jQuery)を使ってメールリンクを生成する記述例です。

@ マークはそのまま記述せず、文字参照の &#64; を使用しています。

以下のスクリプトは外部ファイルとして保存して、読み込むようにします。

jQuery(function($){
  var delimiter = ",";  //分割文字をカンマに設定
  if($('span.nsm').length !== 0) { //メールアドレスがある場合のみ実行    
    if($('span.nsm').text() !== ''){
      var nsm_strings = $('span.nsm').text().split(delimiter);//分割文字で分割
      var pre = $.trim(nsm_strings[0]);  //最初の部分から空白を削除
      var domain = "&#64;" + $.trim(nsm_strings[1]);  
      //@(&#64;)と後ろの部分から空白を削除したものを連結
      var nsm_address =  pre + domain;    //メールアドレスを組み立てる
      $('span.nsm').html('<a href="ma' + 'ilto:' + nsm_address + '">' + nsm_address + '</a>');
    }
  }
});

同じページにメールアドレスが複数必要な場合は、each() メソッド を利用して以下のようにします。

jQuery(function($){
  var delimiter = ",";  //分割文字をカンマに設定
  if($('span.nsm').length !== 0) { //メールアドレスがある場合のみ実行 
    $('span.nsm').each(function() {
      if($(this).text() !== ''){
        var nsm_strings = $(this).text().split(delimiter);  //分割文字で分割
        var pre = $.trim(nsm_strings[0]);  //最初の部分から空白を削除
        var domain = "&#64;" + $.trim(nsm_strings[1]);  
        //@(&#64;)と後ろの部分から空白を削除したものを連結
        var nsm_address =  pre + domain;    //メールアドレスを組み立てる
        $(this).html('<a href="ma' + 'ilto:' + nsm_address + '">' + nsm_address + '</a>');
      }
    }); 
  }
});

表題(subject)付きや表示する文字をメールアドレス以外にする場合に対応

表題(subject)付きや表示する文字をメールアドレス以外にする場合に対応させる方法です。まず、HTML への記述の方法を以下のように決めておきます。

  • メールアドレスを表示する部分は span 要素で記述し、クラスを「nsm」(no spam mail の略)とすることとします(任意の値でかまいません)。
  • メールアドレスの「@」を半角英字のカンマ「,」に換えて記述します。
  • 表題(subject)と表示する文字もそれぞれカンマ「,」で区切って表題、表示する文字の順番で記述します。

以下のように記述するようにします。

<span class="nsm">info, example.com, お問い合わせ, カスタマーサービス</span>

info, example.com, お問い合わせ, カスタマーサービス

表題が不要な場合はその部分を空にしておきます。

<span class="nsm">info, example.com, , カスタマーサービス</span>

info, example.com, , カスタマーサービス

表示する文字と表題も不要な場合はメールアドレス部分をカンマで区切って記述します。

<span class="nsm">info, example.com</span>

info, example.com

以下がスクリプトの記述例です。同じページにメールアドレスが複数必要な場合にも対応しています。

jQuery(function($){
  if($('span.nsm').length !== 0) {
    $('span.nsm').each(function() {
      if($(this).text() !== ''){
        var nsm_strings = $(this).text().split(delimiter);  //分割文字で分割
        nsm_strings = $.map(nsm_strings, function(value){
          return $.trim(value);//分割した文字列から前後の空白を削除
        });       
        var pre = nsm_strings[0];  //アドレスの@マークの前の文字列
        var domain = "&#64;" + nsm_strings[1];  
        //@(&#64;)と後ろの部分を連結
        var nsm_address =  pre + domain;    //メールアドレスを組み立てる
        var addressWithSubject = nsm_address;
        if(nsm_strings[2]){    //表題が指定されていれば
          addressWithSubject += "?subject=" + nsm_strings[2];
        }
        if(nsm_strings[3]){   //表示する文字が指定されていれば
          $(this).html('<a href="ma' + 'ilto:' + addressWithSubject + '">' 
              + nsm_strings[3] + '</a>');
        }else{
          $(this).html('<a href="ma' + 'ilto:' + addressWithSubject + '">' 
              + nsm_address + '</a>');
        }   
      }
    }); 
  }
});

5行目:span 要素内の文字列を分割文字で分割して、それらの文字列の配列を変数に代入しています。

6行目~8行目:$.map() メソッドを使って、配列の要素(分割された文字列)の前後の空白を削除しています。

13行目:変数 addressWithSubject には、アドレス部分と表題部分になります。まずアドレス部分を代入しておきます。

14行目:もし表題が指定されていれば、nsm_strings[2] にその文字列が入っているはずです。その文字列が空白でなければ、addressWithSubject に表題の文字列を追加します。if 文で空白文字や null, undefined は false と判定されます。

17行目:表示する文字(nsm_strings[3])が指定されていれば、それを表示するようにしています。

21行目:表示する文字(nsm_strings[3])が指定されていなければ、メールアドレスを表示します。

クリッカブル・マップを使う場合

以下は、クリッカブル・マップを使ってメールを送信する場合の例です。

この場合、メールリンクは area 要素の href に記述します。

また、クリッカブル・マップを作成する場合、img 要素の usemap 属性は map 要素の id(name) の値に「#」を付けて指定します。

<p id="contactphoto">
  <img src="../images/flower.jpg" usemap="#contactMap" alt="">
  <map id="contactMap" name="contactMap">
    <area alt="contact" href="info,example.com" coords="4,1,494,218" shape="rect">
  </map>
</p>  
var delimiter = ","; 
var nsm_map_strings = $('map#contactMap area').attr('href').split(delimiter);  //分割文字で分割
var pre = $.trim(nsm_map_strings[0]);
var domain = "@" + $.trim(nsm_map_strings[1]);
var nsm_map_address =  pre + domain;	
//"@"はHTML上では @ に変換されるがこの時点では"@"のままなので @ に変換
$('map#contactMap area').attr('href', 'ma' + 'ilto:' + nsm_map_address.replace(/@/, '@'));

contact

スムーズスクロール

ページ内リンクへプラグインを使わずにアニメーションで移動するスムーズスクロールの設定方法です。

animate メソッドと scrollTop プロパティ

画面をスクロールさせる場合 animate メソッドと scrollTop プロパティを利用しますが、どのセレクタに対して実行すべきかはブラウザ(WebKit かそれ以外)により異なります。

  • WebKit : body 要素
  • それ以外 : html 要素
$('body').animate({ scrollTop: 0 }); // WebKit
$('html').animate({ scrollTop: 0 }); // WebKit以外

scrollTop() はメソッドですが、scrollTop は jQueryが用意した CSS 用のプロパティで、 animate() で使用します。値にはトップからの距離を数値(ピクセル。但し px は付けない)で指定します。

以下のように両方を指定して全てのブラウザに対応することができますが、単純なスクロールだけなら問題はないですが、両方指定するとコールバックを指定している場合など、コールバックが2回呼ばれてしまうという副作用があります。

$('html, body').animate({ scrollTop: 0 });

そのためブラウザを判定する必要がありますが、jQuery 1.9 になって $.browser が使えなくなっているため、「使用したい機能が機能するか」で判定する必要があります。

スクロールさせることができる要素が html か body かを判定するには、以下のように実際にどちらの要素でスクロールするかを調べます。

var isHtmlScrollable = (function(){
    var html = $('html'), top = html.scrollTop();
    var el = $('<div/>').height(10000).prependTo('body');
    html.scrollTop(10000);
    //alert(html.scrollTop());  表示される値は 0 または 10000
    var rs = !!html.scrollTop();
    html.scrollTop(top);
    el.remove();
    return rs;
})();

//スクロールのアニメーション
$(isHtmlScrollable ? 'html' : 'body').animate({scrollTop:100}); 
  • 2行目:html 要素の jQuery オブジェクトを変数 html に格納して、現在の top の値を変数 top に格納
  • 3行目:div 要素を生成して高さを 10000 に設定し、body 要素に追加
  • 4行目:html を 10000px スクロールさせる
  • 6行目:html がスクロールしていれば、html.scrollTop() の値は 10000 になり、スクロールしていなければ値は 0 になるので、2重否定(※)で真偽値を変数 rs に格納
  • 7行目:html を最初の高さ(top)に戻す
  • 8行目:生成した div 要素(el)を削除
  • 9行目:真偽値を返す

上記の結果、変数 isHtmlScrollable には、html 要素がスクロールしているかどうかの真偽値が格納されています。

この結果を利用して、13行目のように animate メソッドの対象を html 要素または body 要素に対して実行します。

13行目の isHtmlScrollable ? 'html' : 'body' は条件演算子(三項演算子)を使用して、isHtmlScrollable の値が true であれば html 要素を、false であれば body 要素を使用するように設定しています。

二重否定(※)

“undefined” や object 等が返る可能性がある値を二重否定を行うことで真偽値(Boolean)として取得するイディオムのようなものです。

var foo = undefined;
console.log(foo);    //undefined
console.log(!foo);    //true
console.log(!!foo);    //false
 
var bar = {};
console.log(bar);    // Object {}
console.log(!bar);    //false
console.log(!!bar);    //true

var baz = null;
console.log(baz);    // null
console.log(!baz);    //true
console.log(!!baz);    //false

var zero = 0;
console.log(zero);    // 0
console.log(!zero);    //true
console.log(!!zero);    //false

var empty = "";
console.log(empty);    // 空文字列
console.log(!empty);    //true
console.log(!!empty);    //false

ページトップへの移動

href 属性の値が「#」の場合、ページトップへアニメーションで移動するようにするには、以下のように記述します。

※ jQuery3 を使用する場合の注意: jQuery3 からは、$('a[href=#]') の部分を $('a[href="#"]') のように # を引用符で囲む必要があります。そうしないと「Syntax error, unrecognized expression: [href^=#]」のようなエラーが発生します。(jQuery3 では "#"のみのセレクタは不正な文法としてエラーになることと関係しているかも知れません)

jQuery(document).ready(function($) { 
  $('a[href="#"]').click(function () {
    $(isHtmlScrollable ? 'html' : 'body').animate({
      scrollTop: 0
    }, 500);
      return false;
  });
  
  var isHtmlScrollable = (function(){
    var html = $('html'), top = html.scrollTop();
    var el = $('<div/>').height(10000).prependTo('body');
    html.scrollTop(10000);
    //alert(html.scrollTop());  表示される値は 0 または 10000
    var rs = !!html.scrollTop();
    html.scrollTop(top);
    el.remove();
    return rs;
  })(); 
});

但し、href 属性の値が「#」でも、アニメーションを適用したくない場合もあるので、それらに関しては not() メソッドを使って除外するようにします。

(注意)除外しないと、return false を使ってデフォルトの動作をキャンセルしようとしても、できなくなってしまいます。

以下は、noScroll というクラスを持つ要素を除外する場合の例です。

jQuery(document).ready(function($) { 
  $('a[href="#"]').not('.noScroll').click(function () {
    $(isHtmlScrollable ? 'html' : 'body').animate({
      scrollTop: 0
    }, 500);
      return false;
  });
  
  var isHtmlScrollable = (function(){
    ・・・省略・・・
  })();  
});

ページ内リンクとページトップへの移動

ある要素の id 属性へのリンクへの移動もアニメーションさせるようにします。

<a href=”#someElement” > のような場合にも対応するには以下のようにします。

  • $(“[属性名^=’値’]”):特定の属性が指定した値で始まっている要素
  • not() 除外する要素を指定(除外するものがなければ不要)
  • 要素の href 属性の値を「hrefval」に格納
  • 「hrefval」はページトップの場合「#」、その他の場合は要素の id「#id名」になっています
  • 対象となる要素「targetelement」を生成
  • 要素のポジションを取得
  • その位置までアニメーション
jQuery(document).ready(function($) {
  $('a[href^="#"]').not('アニメーションさせない a 要素').click(function(){
    var hrefval= $(this).attr('href');
    var targetelement = hrefval == "#" ? $('html') : $(hrefval);
    var positiontop = targetelement.offset().top;  //移動先のポジションを取得
    $(isHtmlScrollable ? 'html' : 'body').animate({
      scrollTop: positiontop
    }, 500);
      return false;
  });
  
  var isHtmlScrollable = (function(){
    ・・・省略・・・
  })();  
});

「先頭へ」というリンクを表示

ある程度スクロールすると「先頭へ」というリンクを表示させて、そのリンクをクリックするとページ先頭にアニメーションで移動させるには、以下のようにします。

jQuery(document).ready(function($) {
  $('#footer').after('<div class="tothetop"><a href="#">先頭へ</a></div>');
  var topBtn = $('.tothetop');  
  topBtn.hide();
  $(window).scroll(function () {
    if ($(this).scrollTop() > 500) {
      topBtn.fadeIn();
    } else {
      topBtn.fadeOut();
    }
  });
  //ページ内リンクとページトップへのアニメーション
  $('a[href^="#"]').not('アニメーションさせない a 要素').click(function(){
    ・・・省略・・・
  });
  
  var isHtmlScrollable = (function(){
    ・・・省略・・・
  })();  
});
  
  • フッターなどの最後の要素の後に「先頭へ」というリンク(クラス名:”tothetop”)を追加します。
  • 最初はリンクを非表示にします topBtn.hide();
  • スクロールの値(以下の例では500px)に応じてリンクをアニメーションで表示・非表示にします。
  • その後に前述のアニメーションでの移動に関する記述をします。

「先頭へ」というリンクには、後でスタイルを指定しやすいようにクラスを付与しておきます(この例ではクラス名:”tothetop”)。

2行目は、id 属性が「footer」の要素の後に after() を使って追加していますが、以下のように body 要素に append() を使って追加しても同じです。

$('body').append('<div class="tothetop"><a href="#">先頭へ</a></div>');

スピードやポジションの調整

スピードは animate() メソッドのパラメータにミリ秒で指定していますが、移動する距離に応じてスピードを調整したり、ページ内リンクへの移動の場合は環境によっては位置を調整する必要があるかも知れません。そのような場合は、以下のようにして調整することができます。

以下の例では、スピードの調整で移動距離を30で割っていますが、この値はそれぞれの環境に合わせて調整してください。

位置の調整では、その要素のポジションより少し上にスクロールするようにすると良いと思います。これも環境に合わせて調整してください。

jQuery(document).ready(function($) {
  $('a[href^="#"]').not().click(function(){
    var hrefval= $(this).attr('href');
    var positiontop;
    var speed;
    if(hrefval == "#") {  //Top への移動の場合
      positiontop = 0;
      speed = 200 + $(this).offset().top /30;  //スピードを調整
    }else{  //ページ内リンクへの移動の場合
      var targetelement = $(hrefval);
      positiontop = targetelement.offset().top -120; //位置を調整
      speed = 200 + (positiontop + 120) / 30; //スピードを調整
    }
    $(isHtmlScrollable ? 'html' : 'body').animate({
      scrollTop: positiontop
    },  speed);
    return false;
  });
  
  ・・・省略・・・
});

「ページ先頭へ」というリンクの CSS の例です。固定した位置に表示するため「position: fixed;」は必須です。(jQuery で設定することもできますが)

div.tothetop {
  position: fixed;  /* 必ず fixed に設定します。 */
  right: 5%;
  bottom: 7%;
  z-index: 150;
  opacity: .6;
}
div.tothetop a {
  display: block;
  color: #fff;
  margin: 0;
  background-color: #fdfdfd;
  background-color: #555;
  font-size: 0.9em;
  outline: none;
  text-decoration: none;
  -webkit-border-radius: 30px;
  -moz-border-radius: 30px;
  -ms-border-radius: 30px;
  -o-border-radius: 30px;
  border-radius: 30px;
  -webkit-transition: 0.3s;
  -moz-transition: 0.3s;
  -o-transition: 0.3s;
  transition: 0.3s;
  height: 60px;
  width: 60px;
  line-height: 60px;
  text-align: center;
}
div.tothetop a:hover {
  background-color: #222;
  color: #789ACD;
}
div.tothetop a:active {
  background-color: #888888;
  color: #fff;
  border: 1px solid #a2a2a2;
}

最終的なスクリプトは以下のようになります。

jQuery(document).ready(function($) {
  //「先頭へ」というリンクを表示
  $('body').append('<div class="tothetop"><a href="#">先頭へ</a></div>');
  var topBtn = $('.tothetop');  
  topBtn.hide();
  $(window).scroll(function () {
    if ($(this).scrollTop() > 500) {
      topBtn.fadeIn();
    } else {
      topBtn.fadeOut();
    }
  });
  
  //スクロールのアニメーション
  $('a[href^="#"]').not().click(function(){
    var hrefval= $(this).attr('href');
    var positiontop;
    var speed;
    if(hrefval == "#") {  //Top への移動の場合
      positiontop = 0;
      speed = 200 + $(this).offset().top /30;  //スピードを調整
    }else{  //ページ内リンクへの移動の場合
      var targetelement = $(hrefval);
      positiontop = targetelement.offset().top -120; //位置を調整
      speed = 200 + (positiontop + 120) / 30; //スピードを調整
    }
    $(isHtmlScrollable ? 'html' : 'body').animate({
      scrollTop: positiontop
    },  speed);
    return false;
  });
  
  //ブラウザの機能判定
  var isHtmlScrollable = (function(){
    var html = $('html'), top = html.scrollTop();
    var el = $('<div/>').height(10000).prependTo('body');
    html.scrollTop(10000);
    var rs = !!html.scrollTop();
    html.scrollTop(top);
    el.remove();
    return rs;
  })(); 
});

ページ外へのリンク

ページ内リンクではなく、外部からページのアンカー(#)へアクセスがあった場合にスムーズスクロールする方法です。

URL の # を含むアンカーの文字列(ハッシュ)は Location オブジェクトの hash プロパティで取得できます。

取得したハッシュが空文字でなければ、それを元にそのポジション target.offset().top を取得して animate() でアニメーションを実行します。

var hash = window.location.hash;  //Location オブジェクト hash プロパティ
if (hash !== "") {
  var target    = $(hash);
  var pos    = target.offset().top - 100;  //100px 上に調整
  $(isHtmlScrollable ? 'html' : 'body').animate({      
    scrollTop: pos
  }, 800);
}    

ページ内リンクの場合はページの読み込みが完了しているので jQuery の処理なども終了していますが、外部からページへアクセスした場合は記述する位置によっては、他の jQuery の処理などに影響されて期待した位置にスクロールされない可能性があります。

その場合は、記述する位置を他の JavaScript の処理の後にしてみると良いかも知れません。

スライドショー

スライドショーのサンプルです。

関連ページ:Bootstrap4 カルーセル

クロスフェードを使ったスライドショー

Beach

Beach

  • Beach
  • Clear water
  • Bay view
  • Cloud in the sky
  • Pool side
  • Palm tree

以下は、スライドショーの HTML です。

<div id="mainphoto"> <img src="../images/001.jpg" width="480" height="360" alt="Beach">
  <p id="title">Beach</p>
</div><!-- end of #mainphoto -->

<ul id="photolist">
  <li><img src="../images/001.jpg" alt="Beach"></li>
  <li><img src="../images/002.jpg" alt="Clear water"></li>
  <li><img src="../images/003.jpg" alt="Bay view"></li>
  <li><img src="../images/004.jpg" alt="Cloud in the sky"></li>
  <li><img src="../images/005.jpg" alt="Pool side"></li>
  <li><img src="../images/006.jpg" alt="Palm tree"></li>
</ul>  

id が mainphoto の div 要素に写真を表示します。最初に表示する写真を配置しておき、絶対配置に設定します。

id が title の p 要素には、写真のタイトルを表示します。写真のタイトルは、それぞれの img 要素の alt 属性の値を使用します。

id が photolist の ul 要素にスライドショーで使用する写真を記述しておき、この部分は非表示にしておきます。

以下が、CSS です。このスライドショーでは、480px X 360px の画像を使用しています。

#mainphoto {
  width: 480px;
  height: 360px;
  padding: 0;
  margin: 20px auto;
  position: relative;  /*---基点とします。---*/
  border: 5px solid #CCC;
  
}
/*---img 要素---*/
#mainphoto img {
  position: absolute;  /*---絶対配置します---*/
  top: 0;
  left: 0;
}
/*---写真のタイトルを表示する p 要素---*/
p#title {
  position: absolute;
  z-index: 10;
  bottom: 10px;
  right: 30px;
  font-size: 11px;
  color: #FFF;  
}
 
/*---表示する写真を記述しておく ul 要素---*/
#photolist {
  display: none;
}

以下は、スライドショーの基本的な部分の jQuery の記述です。

変数 nextphoto は、次に表示する img 要素のインデックスを格納します。最初は 0 にしておきます。

変数 photolist$ は、id が photolist の ul 要素内に記述されている img 要素のラップ集合です。

変数 photocount は、表示する画像要素の総数になります。

showphoto() はスライドショーを表示するための関数です。

変数 src、alt は次に表示する画像の src 属性と alt 属性を eq() を使って、取得しています。

変数 nextimg は次に表示する img 要素の HTML を生成しています。

jQuery(function($){
  var nextphoto = 0;  //次に表示する写真のインデックス
  var photolist$ = $('#photolist li img');  //画像要素のラップ集合
  var photocount = photolist$.length;  //画像要素の数
  var timer;
  function showphoto() {  //スライドショーの関数
    var src = photolist$.eq(nextphoto).attr('src');
    var alt = photolist$.eq(nextphoto).attr('alt');
    var nextimg = '<img src="' + src + '" width="480" height="360" alt="' + alt + '">';
    $('#mainphoto img').before(nextimg);
    $('#title').text(alt).hide().fadeIn(1000);
    $('#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
    });
    nextphoto = ( ++ nextphoto) % photocount;
    timer = window.setTimeout(showphoto,3000);
  }
  showphoto();
  
  $(window).unload(function(){
    window.clearTimeout(timer);
  }); 
 
});

10行目では、before() を使って、現在表示されている img 要素の前に、次に表示する画像を配置します。この時、img 要素は絶対配置を指定しているので、2つの画像は完全に重なって配置されます。(現在表示されている画像が手前になります)

11行目は、画像のタイトルをフェードインで表示しています。

12行目は、手前に表示されている画像を fadeOut() でフェードアウトして非表示にしていきます。これにより、10行目で配置された画像が少しずつ表示されて、クロスフェード効果ができます。

フェードアウトが完了しても、元の img 要素は display プロパティが none になっただけで HTML 要素としては存在しているので、13行目のコールバック関数内で、remove() を使って削除しています。

15行目は、次に表示する画像のインデックスを1つ増やしてします。

16行目は、window.setTimeout() を使って繰り返し処理するように設定しています。

20行目、21行目は、ページを閉じた際に、一応念のため、繰り返し処理を停止しています。

以下は、ここまでの記述で表示されるスライドショーのサンプルのリンクです。

スライドショーサンプル1

また、上記の jQuery の記述は、window.setInterval() を使って以下のように記述することも可能です。

jQuery(function($){
  var nextphoto = 1;
  var photolist$ = $('#photolist li img');
  var photocount = photolist$.length;
  function showphoto() {
    var src = photolist$.eq(nextphoto).attr('src');
    var alt = photolist$.eq(nextphoto).attr('alt');
    var nextimg = '<img src="' + src + '" width="480" height="360" alt="' + alt + '">';
    $('#mainphoto img').before(nextimg);
    $('#title').text(alt).hide().fadeIn(1000);
    $('#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
    });
    nextphoto = ( ++ nextphoto) % photocount;
    //nextphoto ++;
    //if(nextphoto == photocount ) nextphoto = 0;
  }
  var timer = window.setInterval(showphoto, 3000);
  
});

コントロールの追加

続いて、再生ボタンやそれぞれの画像に対応する丸いアイコンを追加します。これらは、直接 HTML には記述せず、jQuery で追加します。

以下は、再生ボタンや丸いアイコンの CSS です。

#icons_wrapper {
  position: relative;
  width: 480px;
  height: 80px;
  margin: auto;
}
#icons {
  width: 20px;
  margin: 0 auto;
}
.icon {
  display: inline-block;
  background-color: #FFF;
  width: 8px;
  height: 8px;
  margin:5px 5px -20px 10px;
  cursor: pointer;
  border: 1px solid #999;
  border-radius: 5px;
}
.icon.icon_selected {
  background-color: #666;
  cursor: default;
}
.icon.icon_hover {
  border: 1px solid #666;
  background-color: #CCC;
}
#start {
  display: none;
  cursor: pointer;
  color: #999;
  position: absolute;
  top: 5px;
  left: 350px;
}

以下は、再生ボタンや丸いアイコンを追加する jQuery の記述です。

丸いアイコンは、for 文で append() 使って画像の数だけ生成します。

var div_icons_wrapper$ = $('<div id="icons_wrapper"></div>');
var div_icons$ = $('<div id="icons"></div>');
div_icons_wrapper$.append(div_icons$);
div_icons_wrapper$.append('<p id="start"><i class="fa fa-play-circle"></i></p>');
$('#mainphoto').after(div_icons_wrapper$ );
for(var i = 0; i < photocount; i++){
  var icon_alt = photolist$.eq(i).attr('alt');
  div_icons$.append('<div class="icon" title="' + icon_alt + '">');
}
var icon$ = $('div.icon');
var icon_count = icon$.length;
div_icons$.width(25 * icon_count);
icon$.eq(nextphoto).attr('class', 'icon icon_selected');

上記の jQuery の記述により、以下のような HTML が生成されます。

<div id="icons_wrapper">
  <div id="icons">
    <div class="icon icon_selected" title="Beach"></div>
    <div class="icon" title="Clear water"></div>
    <div class="icon" title="Bay view"></div>
    <div class="icon" title="Cloud in the sky"></div>
    <div class="icon" title="Pool side"></div>
    <div class="icon" title="Palm tree"></div>
  </div>
  <p id="start"> <i class="fa fa-play-circle"></i> </p>
</div>

以下は、jQuery の全文です。

丸いアイコンをクリックすると、そのアイコンに対応する画像をクロスフェードで表示するように関数 cross_fade() を追加しています。

また、スクリプトで追加した要素のクリックやマウスオーバーイベントを on() を使って設定しています。

jQuery(function($){
  
  var nextphoto = 0;
  var photolist$ = $('#photolist li img');
  var photocount = photolist$.length;
  var timer;
  
  var div_icons_wrapper$ = $('<div id="icons_wrapper"></div>');
  var div_icons$ = $('<div id="icons"></div>');
  div_icons_wrapper$.append(div_icons$);
  div_icons_wrapper$.append('<p id="start"><i class="fa fa-play-circle"></i></p>');
  $('#mainphoto').after(div_icons_wrapper$ );
  for(var i = 0; i < photocount; i++){
    var icon_alt = photolist$.eq(i).attr('alt');
    div_icons$.append('<div class="icon" title="' + icon_alt + '">');
  }
  var icon$ = $('div.icon');
  var icon_count = icon$.length;
  div_icons$.width(25 * icon_count);
  icon$.eq(nextphoto).attr('class', 'icon icon_selected');
  
  function showphoto() {
    cross_fade(nextphoto);
    nextphoto = ( ++ nextphoto) % photocount;
    timer = window.setTimeout(showphoto,3000);
  }
  showphoto();
  
  var is_animated;
  function cross_fade(index) {
    is_animated = true;
    icon$.attr('class', 'icon').css({cursor: 'pointer'});
    icon$.eq(index).attr('class', 'icon icon_selected').css({cursor: 'default', opacity: 0.4}).animate({opacity: 1.0}, 1300);	
    var src = photolist$.eq(index).attr('src');
    var alt = photolist$.eq(index).attr('alt');
    var nextimg = '<img src="' + src + '" width="480" height="360" alt="' + alt + '">';
    $('#mainphoto img').before(nextimg);
    $('#title').text(alt).hide().fadeIn(1000);
    $('#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
      is_animated = false;
    });
  }
  
  $(document).on('click', '#start', function() {
    showphoto();
    $(this).css("display", "none");
    return false;
  });
  
  $(document).on('click', 'div.icon', function() {
    if($(this).hasClass('icon_selected') || is_animated) {
      return false;
    }
    var icon_index = icon$.index($(this));
    window.clearTimeout(timer);
    cross_fade(icon_index);
    nextphoto = icon_index;	
    $('#start').css("display", "block");
  });
  
  $(document).on('mouseover', 'div.icon', function() {
    if(!$(this).hasClass('icon_selected')) {
      $(this).addClass('icon_hover');
    }		
  });
  
  $(document).on('mouseout', 'div.icon', function() {
    if(!$(this).hasClass('icon_selected')) {
      icon$.removeClass('icon_hover');
    }		
  });
  
  $(window).unload(function(){
    window.clearTimeout(timer);
  }); 
 
});

以下は、関数 cross_fade() の記述です。

変数 is_animated は、現在アニメーション中かどうかを判定するために使用します(アニメーション中に、アイコン画像をクリックされると問題があるため)。

関数 cross_fade() では、画像のインデックスを引数に取ります。

最初に、変数 is_animated を true に設定してアニメーション中であることにします。

4行目では、アイコン画像の CSS をリセットしています。

5行目では、インデックスに対応するアイコンの画像にクラス「icon_selected」を設定して表示を変更し、さらにアニメーションで透明度を変更しています。

後は、showphoto() のクロスフェードと同じ処理です。

但し、fadeOut() のコールバック関数の中で、変数 is_animated を false に変更しています。

var is_animated;
function cross_fade(index) {
  is_animated = true;
  icon$.attr('class', 'icon').css({cursor: 'pointer'});
  icon$.eq(index).attr('class', 'icon icon_selected').css({cursor: 'default', opacity: 0.4}).animate({opacity: 1.0}, 1300);	
  var src = photolist$.eq(index).attr('src');
  var alt = photolist$.eq(index).attr('alt');
  var nextimg = '<img src="' + src + '" width="480" height="360" alt="' + alt + '">';
  $('#mainphoto img').before(nextimg);
  $('#title').text(alt).hide().fadeIn(1000);
  $('#mainphoto img:last').fadeOut(1000, function(){
    $(this).remove();
    is_animated = false;
  });
}

以下は、丸いアイコンをクリックした際のイベント処理です。この要素は、スクリプトで追加しているので on() を使う必要があります。

2~4行目では、もしクリックされたアイコンのクラスが「icon_selected」である場合と、アニメーション中の場合は何もしないで終了します。

変数 icon_index にクリックされたアイコンのインデックスを格納します。

6行目で、スライドショーの繰り返し処理を停止しています。

続いて関数 cross_fade(icon_index) を実行して、クリックされたアイコンに対応する画像をクロスフェードで表示します。

変数 nextphoto に icon_index の値を代入します。

スライドショーは停止されているので、最後にスタートボタンを表示します。

$(document).on('click', 'div.icon', function() {
  if($(this).hasClass('icon_selected') || is_animated) {
    return false;
  }
  var icon_index = icon$.index($(this));
  window.clearTimeout(timer);
  cross_fade(icon_index);
  nextphoto = icon_index;	
  $('#start').css("display", "block");
});

以下は、スライドショーサンプルのリンクです。

スライドショーサンプル2

スライダー

以下はスライダーのサンプルです。

  • Beach
  • Clear water
  • Bay view
  • Cloud in the sky
  • Pool side
  • Palm tree

以下は、スライダーの HTML です。

id が slider の div 要素に写真を表示します。

id が photo_list の ul 要素内の li 要素にスライダーで表示する画像要素を記述しておき、CSS で float: left を指定します。

id が slider_inner の div 要素は、配下の li 要素がフロートで横並びになっているので、画像が一列に並んだものが配置されています。この要素の幅は画像の枚数が変わっても良いように jQuery で指定します。

<div id="slider_wrap">
  <div id="slider">
    <div id="slider_inner">
      <ul id="photo_list">
        <li><img src="../images/001.jpg" alt="Beach"></li>
        <li><img src="../images/002.jpg" alt="Clear water"></li>
        <li><img src="../images/003.jpg" alt="Bay view"></li>
        <li><img src="../images/004.jpg" alt="Cloud in the sky"></li>
        <li><img src="../images/005.jpg" alt="Pool side"></li>
        <li><img src="../images/006.jpg" alt="Palm tree"></li>
      </ul>
    </div><!-- end of #slider_inner --> 
  </div><!-- end of #slider --> 
</div><!-- end of #slider_wrap --> 

以下が、CSS です。このスライドショーでは、480px X 360px の画像を使用しています。

#slider は画像を表示する領域です。画像の横幅は 480px ですが、左右に 5px ずつのマージンと、5px のボーダーがあるので幅は 500px に設定します。高さは 5px のボーダーがあるので 370px に設定します。

#slider には、overflow: hidden を指定して、領域をはみ出した部分は非表示にします。

#photo_list は ul 要素ですが、配下の li 要素をフロートで横並びにしているのと、画像の枚数が変わる可能性があるので横幅は指定しません。

ul, li {
  margin: 0;
  padding: 0;
}
#slider_wrap {
  margin: 20px auto;
  width: 500px;
  height: 370px;
  padding: 20px;
}
#slider {
  width: 500px;
  height: 370px;
  overflow: hidden;
}
#photo_list {
  /*幅は指定しない*/
  height: 370px;
  list-style-type: none;
}
#photo_list li {
  float: left;
  margin: 0 5px;
  cursor: pointer;
}
#photo_list img {
  border: 5px solid #ccc;
}

以下はスライダーのイメージです。

id が slider_inner の div 要素を、animate() で左マージン(marginLeft)の値を変更して、左側に移動させます。

左側に移動して、非表示になっている部分(#photo_list li:first)を appendTo() を使って、最後に移動させます。

animate() で変更した左マージン(marginLeft)の値を元の値(0px)に戻します。

slide の説明

以下が jQuery の記述です。

4行目は、li 要素の総数を変数 li_count に代入しています。これは表示する写真の数と同じです。

6行目では、li 要素1つの幅を求めています。li 要素の幅自体は width() で取得できますが、実際はマージンが設定してあるのでその分も幅に加えます。

parseInt() は文字列を数値に変換する JavaScript のメソッドです。css('margin-left') で取得できる値は "200px" のような文字列なので、計算に使用するために数値に変換する必要があります。

9行目では、id が slider_inner の div 要素の幅を設定しています。6行目で取得した li 要素の幅を使用して全体の幅を設定しています。

jQuery(function($){
  var photo_list$ = $('#photo_list');
  var li$ = $('#photo_list li');
  var li_count = li$.length;  //写真の数
  //li 要素の幅 + その左右マージン
  var li_width = li$.width() + parseInt(li$.css('margin-left'), 10) + parseInt(li$.css('margin-right'), 10);
  var slider_inner$ = $('#slider_inner');
  //slider_innerの幅を設定(li 要素の幅 * その個数)
  slider_inner$.css('width', (li_width * li_count) + 'px');  
  
  var timer;
  function slideshow() {
     slider_inner$.stop().animate({
      marginLeft: parseInt(slider_inner$.css('margin-left'), 10) - li_width + 'px'
    }, 800, 'easeInOutExpo',
    function(){
      slider_inner$.css('margin-left', '0px');
      $('#photo_list li:first').appendTo(photo_list$);
    }); 
    timer = window.setTimeout(slideshow,2500); 
  }
  var start_timer = window.setTimeout(slideshow,2500); 
 
});   

12行目からはスライダー(スライドショー)の関数 slideshow() の設定です。

animate() で、marginLeft の値を li 要素の幅1つ分左に移動させます。また、表示に効果を与えるためイージング easing に「easeInOutExpo」を設定しています。

animate() のコールバック関数では、marginLeft の値を元に戻して、左側に移動した li 要素を #photo_list の一番最後に移動させています。

window.setTimeout(slideshow,2500) で繰り返し処理するように設定します。

以下は、ここまでの記述で表示されるスライダーのサンプルのリンクです。

スライドショーサンプル3

コントロールの追加

続いて、再生ボタンやそれぞれの画像に対応する丸いアイコンを追加します。これらは、直接 HTML には記述せず、jQuery で追加します。(前述のサンプルと同じです)

以下は、再生ボタンや丸いアイコンの CSS です。

#icons_wrapper {
  position: relative;
  width: 480px;
  height: 80px;
  margin: auto;
}
#icons {
  width: 20px;
  margin: 0 auto;
}
.icon {
  display: inline-block;
  background-color: #FFF;
  width: 8px;
  height: 8px;
  margin:5px 5px -20px 10px;
  cursor: pointer;
  border: 1px solid #999;
  border-radius: 5px;
}
.icon.icon_selected {
  background-color: #666;
  cursor: default;
}
.icon.icon_hover {
  border: 1px solid #666;
  background-color: #CCC;
}
#start {
  display: none;
  cursor: pointer;
  color: #999;
  position: absolute;
  top: 5px;
  left: 350px;
}

12行目は、次の画像のインデックスを変数「nextphoto」に代入しています(最初は 0 で初期化しておきます)。

13行目から25行目でコントロールを追加しています(クロスフェードのサンプルと同じ)。

28行目の変数「is_animated」はアニメーション中かどうかを判定するものです(アニメーション中に、アイコン画像をクリックされると問題があるため)。

30行目では、変数「is_animated」に true を設定して、アニメーション中であるとします。

31行目は、変数「nextphoto」の値を1増加しています。

32行目は、一旦全てのアイコン画像の CSS をデフォルトに設定しています。

33行目では、変数「nextphoto」の値に対応するアイコン画像の要素にクラス「icon_selected」を追加してアニメーションを設定しています。

34行目から41行目はスライダーのアニメーションの記述です。

46行目から50行目は、スタートボタンをクリックした際の記述で、slideshow() を呼び出してスライダーを開始させています。

52行目から86行目は、各画像に対応するアイコン画像をクリックした際の処理の記述です(詳細は別途記述)。

88行目から98行目は、アイコン画像にマウスオーバーした際の処理の記述です(クラスの追加及び削除でスタイルを変更)。

100行目から103行目は、ページを閉じた際に繰り返し処理を終了させる記述です。

jQuery(function($){
  var photo_list$ = $('#photo_list');
  var li$ = $('#photo_list li');
  //写真の数
  var li_count = li$.length;
  //li 要素の幅 + その左右マージン
  var li_width = li$.width() + parseInt(li$.css('margin-left'), 10) + parseInt(li$.css('margin-right'), 10);
  var slider_inner$ = $('#slider_inner');
  //slider_innerの幅を設定(li 要素の幅 * その個数)
  slider_inner$.css('width', (li_width * li_count) + 'px');
  
  var nextphoto = 0; 
  var div_icons_wrapper$ = $('<div id="icons_wrapper"></div>');
  var div_icons$ = $('<div id="icons"></div>');
  div_icons_wrapper$.append(div_icons$);
  div_icons_wrapper$.append('<p id="start"><i class="fa fa-play-circle"></i></p>');
  $('#slider_wrap').append(div_icons_wrapper$ );
  for(var i = 0; i < li_count; i++){
    var icon_alt = li$.eq(i).find("img").attr('alt');
    div_icons$.append('<div class="icon" title="' + icon_alt + '">');
  }
  var icon$ = $('div.icon');
  var icon_count = icon$.length;
  div_icons$.width(25 * icon_count);
  icon$.eq(nextphoto).attr('class', 'icon icon_selected');
  
  var timer;
  var is_animated;
  function slideshow() {
    is_animated = true;
    nextphoto = ( ++ nextphoto) % li_count;
    icon$.attr('class', 'icon').css({cursor: 'pointer'});
    icon$.eq(nextphoto).attr('class', 'icon icon_selected').css({cursor: 'default', opacity: 0.4}).animate({opacity: 1.0}, 1300);	
    slider_inner$.stop().animate({
        marginLeft: parseInt(slider_inner$.css('margin-left'), 10) - li_width + 'px'
      }, 800, 'easeInOutExpo',
      function(){
        slider_inner$.css('margin-left', '0px');
        $('#photo_list li:first').appendTo(photo_list$);
        is_animated = false;
    });  
    timer = window.setTimeout(slideshow,2500); 
  }
  var start_timer = window.setTimeout(slideshow,2500); 
  
  $(document).on('click', '#start', function() {
    slideshow();
    $(this).css("display", "none");
  });
  
  $(document).on('click', 'div.icon', function() {
    if($(this).hasClass('icon_selected') || is_animated) {
      return false;
    }
    is_animated = true;
    var icon_index = icon$.index($(this));
    window.clearTimeout(start_timer);
    window.clearTimeout(timer);
    icon$.attr('class', 'icon').css({cursor: 'pointer'});
    icon$.eq(icon_index).attr('class', 'icon icon_selected').css({cursor: 'default', opacity: 0.4}).animate({opacity: 1.0}, 1300);	
    var diff = icon_index - nextphoto;
    var is_diff_negative = diff < 0 ? true : false;
    diff = Math.abs(diff);
    if(is_diff_negative) {
      for(var i = 0; i < diff; i ++) {
        $('#photo_list li:last').prependTo(photo_list$);
        slider_inner$.css('margin-left', '-' + li_width * diff + 'px');
      } 
    }   
    var dist = is_diff_negative ? li_width * diff * -1 : li_width * diff
    slider_inner$.stop().animate({
      marginLeft: parseInt(slider_inner$.css('margin-left'), 10) - dist + 'px'
    }, 800, 'easeInOutExpo',
    function(){    
      if(!is_diff_negative) {
        for(var i = 0; i < diff; i ++) {
          $('#photo_list li:first').appendTo(photo_list$);
        }
      }
      slider_inner$.css('margin-left', '0px');
      is_animated = false;  
    });
    nextphoto = icon_index;	
    $('#start').css("display", "block");
  });
  
  $(document).on('mouseover', 'div.icon', function() {
    if(!$(this).hasClass('icon_selected')) {
      $(this).addClass('icon_hover');
    }		
  });
  
  $(document).on('mouseout', 'div.icon', function() {
    if(!$(this).hasClass('icon_selected')) {
      icon$.removeClass('icon_hover');
    }		
  });
  
  $(window).unload(function(){
    window.clearTimeout(start_timer);
    window.clearTimeout(timer);
  });   
});

アイコン画像をクリックした際の処理

以下は、前述のスクリプトの52行目から86行目を抜粋したものです。

2行目から4行目は、クリックしたアイコンにクラス「icon_selected」が設定されているか、またはアニメーション中の場合(is_animated が true の場合)は何もせずに、終了する記述です。

5行目は、変数「is_animated」に true を設定して、アニメーション中であるとしています。

6行目は、index() を使ってクリックされたアイコン画像のインデックスを取得して変数「icon_index」に代入しています。

7行目、8行目は、clearTimeout() でスライダーの繰り返し処理を停止しています。

9行目、10行目は、アイコン画像のスタイルを初期化して、その後アニメーションでアイコン画像の透明度を変更しています。

11行目は、クリックされたアイコン画像のインデックスと、次に表示する画像のインデックスの差分を変数「diff」に代入しています。

12行目は、条件演算子(三項演算子)を使ってインデックスの差分「diff」の値が正の値か負の値かを判定して、負の場合は変数「is_diff_negative」に true を、正の場合は false を設定しています。

13行目は、「diff」の値の絶対値を取得しています(後の処理の計算で使用するため)。

14行目から19行目は、変数「is_diff_negative」の値が true (負の値)の場合の処理です。

変数「is_diff_negative」の値が true (負の値)の場合、スライドする方向が逆になるため、画像を移動させる必要があります。

20行目では、条件演算子(三項演算子)を使って移動(スライド)する距離(marginLeft)を算出して変数「dist」に代入しています。変数「is_diff_negative」の値が true (負の値)の場合は -1 をかけて、方向を逆転しています。

21行目から32行目はスライダーのアニメーションの記述です。

31行目では、アニメーションが終了しているので、変数「is_animated」に false を設定しています。

$(document).on('click', 'div.icon', function() {
  if($(this).hasClass('icon_selected') || is_animated) {
    return false;
  }
  is_animated = true;
  var icon_index = icon$.index($(this));
  window.clearTimeout(start_timer);
  window.clearTimeout(timer);
  icon$.attr('class', 'icon').css({cursor: 'pointer'});
  icon$.eq(icon_index).attr('class', 'icon icon_selected').css({cursor: 'default', opacity: 0.4}).animate({opacity: 1.0}, 1300);	
  var diff = icon_index - nextphoto;
  var is_diff_negative = diff < 0 ? true : false;
  diff = Math.abs(diff);
  if(is_diff_negative) {
    for(var i = 0; i < diff; i ++) {
      $('#photo_list li:last').prependTo(photo_list$);
    } 
    slider_inner$.css('margin-left', '-' + li_width * diff + 'px');
  }   
  var dist = is_diff_negative ? li_width * diff * -1 : li_width * diff
  slider_inner$.stop().animate({
    marginLeft: parseInt(slider_inner$.css('margin-left'), 10) - dist + 'px'
  }, 800, 'easeInOutExpo',
  function(){    
    if(!is_diff_negative) {
      for(var i = 0; i < diff; i ++) {
        $('#photo_list li:first').appendTo(photo_list$);
      }
    }
    slider_inner$.css('margin-left', '0px');
    is_animated = false;  
  });
  nextphoto = icon_index;	
  $('#start').css("display", "block");
});

逆方向に画像を2つ分スライドさせるには、以下のようにまず画像を2つ移動させ、margin-left の値をその分設定して、その後2つ分の距離スライドさせます。(14行目~19行目)

slide の説明

通常の方向に画像を2つ分スライドさせるには、以下のように画像を2つ分の距離スライドさせて、その後2つの画像を移動させます。(25行目~30行目)

slide の説明

ページの最終更新日を表示

JavaScript を使って Web ページの最終更新日時を取得するには、document.lastModified プロパティ(Document オブジェクトのプロパティ)を使います。

但し、有効な値を取得できるのは、静的な HTML の場合で、PHP などの動的なファイルの場合、取得できるのは現在時刻になってしまいます。

また、document.lastModified は HTTP レスポンスヘッダの「Last-Modified」の値になるため、サーバ側のレスポンスに依存します。「Last-Modified」がない場合は、現在の時刻が返されたりします。

例えば、以下のような HTML がある場合、次のような コードを記述すると、そのページの最終更新日が表示されます。

HTML
<p id="lm_date"></p>
jQuery
$("#lm_date").text("最終更新:" + document.lastModified);

上記が表示結果ですが、document.lastModified で返されるのは、Date オブジェクトでなく文字列です。

日時の形式を変更するには、Date オブジェクトのコンストラクタとメソッドを使って以下のようにします。getMonth() で取得する値は 0~11 なので1を足します。

var modified = new Date(document.lastModified);
var year = modified.getFullYear();
var month= modified.getMonth() + 1;
var date = modified.getDate();
$("#lm_date2").text("最終更新日:" + year + "年" + month + "月" + date + "日");

例えば、全てのページで読み込む共通の JavaScript ファイルがあれば、そのファイルに以下を記述すれば、全てのページの content と言う id の要素の先頭に最終更新日を表示することができます。

var modified = new Date(document.lastModified);
var year = modified.getFullYear();
var month= modified.getMonth() + 1;
var date = modified.getDate();
$("#content").prepend("<p id='last_modified'>最終更新:" + year + "年" + month + "月" + date +  "日</p>");

以下は、サイト内に PHP のファイルがあり、それらのファイルには更新日時を表示しないようにする場合の例です。

isPHP() は、現在のファイルの拡張子が php かどうかを判定する独自関数です。また、この例では更新日を表示する id 属性が「last_modified」の p 要素がすでに存在する場合は出力しないようにしています。

function isPHP() {
  var filename = window.location.href.match(".+/(.+?)([\?#;].*)?$")[1];
  var file_extension =  filename.split('.')[1];
  if(file_extension === "php") {
    return true;
  }
}

var modified = new Date(document.lastModified);
var year = modified.getFullYear();
var month= modified.getMonth() + 1;
var date = modified.getDate();


if(!isPHP() && $("#last_modified").length === 0) {
  $("#content").after("<p id='last_modified'>最終更新:" + year + "年" + month + "月" + date +  "日</p>");
}

現在のページのファイル名は、Location オブジェクトから取得することができます。
(関連項目:ファイル名の取得

ファイルの拡張子は、split() メソッドを使ってファイル名から取得しています。

PHP を使った更新日の表示は「ディレクトリとファイルの操作/更新日 getlastmod()」を参照ください。

HTML特殊文字変換

「&」や「<」 「>」 「"」 「'」 の文字を JavaScript の正規表現を使って、それぞれ &amp;, &lt;, &gt;, &quot;, &#39; に変換する方法です。

以下は受け取った値の HTML 特殊文字を変換する関数の例です。

受け取った文字列を replace() メソッドを使って変換しています(5行目)。

第1パラメータの検索する文字列の正規表現には、RegExp() コンストラクタ関数を使用します(正規表現リテラルを使ってはうまく行きません)。

function escape_html(val) {
  var targets = ["&", "<", ">" ,'"', "'"];
  var escapes = ["&amp;", "&lt;", "&gt;", "&quot;", "&#39;"];
  for(var i=0; i<targets.length; i++){ 		 
    val = val.replace(new RegExp(targets[i], 'g'), escapes[i]);
  }	
  return val; 
}

[追記]上記はもっと簡単に記述することができます。

function escape_spacial_chars(str){
  return str.replace(/&/g,"&amp;")
    .replace(/"/g,"&quot;")
    .replace(/'/g,"&#39;")
    .replace(/</g,"&lt;")
    .replace(/>/g,"&gt;");
}

以下は上記の関数を使って入力された文字列の特殊文字を変換して出力する例です。

以下は HTML のマークアップです。Bootstrap のクラスを指定してスタイルを適用しています。

<div id="entity_convertor" style="max-width: 500px;">
  <div>
    <input type="text" id="input" class="form-control" placeholder="変換するテキスト">
  </div>
  <button id="convert" class="btn btn-primary btn-sm">変換</button>
  <button id="clear" class="btn btn-success btn-sm">クリア</button>
  <div>
    <pre id="escapeHTML"><span>変換結果</span></pre>
  </div>
</div>

以下は jQuery の記述です。

jQuery(function($){
  function escape_html(val) {
    var targets = ["&", "<", ">" ,'"', "'"];
    var escapes = ["&amp;", "&lt;", "&gt;", "&quot;", "&#39;"];
    for(var i=0; i<targets.length; i++){ 		 
      val = val.replace(new RegExp(targets[i], 'g'), escapes[i]);
    }	
    return val; 
  }

  $('#convert').click(function() {
    var converted = escape_html($('#input').val()) ;	
    $('#escapeHTML').text(converted).css('display', 'block');
  });	

  $('#clear').click(function() {
    $('#input').val('');	
    $('#escapeHTML').html('<span>変換結果</span>');
  });
});

[追記]以下の方が簡潔です(サニタイズする関数を書き換えたもの)。

jQuery(function($){
  function escape_spacial_chars(str){
    return str.replace(/&/g,"&amp;")
      .replace(/"/g,"&quot;")
      .replace(/'/g,"&#39;")
      .replace(/</g,"&lt;")
      .replace(/>/g,"&gt;");
  }
 
  $('#convert').click(function() {
    var converted = escape_spacial_chars($('#input').val()) ;	
    $('#escapeHTML').text(converted).css('display', 'block');
  });	
 
  $('#clear').click(function() {
    $('#input').val('');	
    $('#escapeHTML').html('<span>変換結果</span>');
  });
});
変換結果

関連ページ:HTML特殊文字変換ツールの作成方法

消費税計算ツール

消費税の税抜き価格と税込み価格を算出するツールの作成方法について。

以下のページを参照ください。

消費税計算ツール