20190203のHTMLに関する記事は3件です。

JavaScriptでストップウォッチ作ってみる

参考

javascriptでストップウォッチを作ってみる。忘備録

https://qiita.com/ryomaDsakamoto/items/c49a9d4cd2017405af1b

完成形

stopwatch.png

シンプルなストップウォッチです.

HTML

最低限必要な表示する領域とボタンを配置します.

stopwatch.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>StopWatch</title>
  </head>
  <body>
    <div id="timer">00:00:000</div>
    <button id="start">start</button> <button id="stop">stop</button>
    <button id="reset">reset</button>
    <script src="js/main.js"></script>
  </body>
</html>

JavaScript(ES6)でロジックを書く

main.js
const timer = document.getElementById("timer");
const start = document.getElementById("start");
const stop = document.getElementById("stop");
const reset = document.getElementById("reset");

// 経過時間を保存する変数(単位:ミリ秒)
let elapsedTime;
// スタートボタンを押したときのUnix Epoch
let startTime;
// タイマーのID
let timerId;
// 以前 stop したタイミングまでの計測時間
let timeToAdd = 0;

// 表示される内容をアップデートする関数
const updateTimeText = () => {
  // 1分 = 1000 ミリ秒 * 60秒
  let m = Math.floor(elapsedTime / (1000 * 60));
  // 1分に満たなかったミリ秒のうち,秒となったもの
  let s = Math.floor((elapsedTime % (1000 * 60)) / 1000);
  // 1秒になれなかったもの
  let ms = elapsedTime % 1000;

  // ゼロパディング
  m = `0${m}`.slice(-2);
  s = `0${s}`.slice(-2);
  ms = `00${ms}`.slice(-3);

  timer.textContent = `${m}:${s}:${ms}`;
};

// 経過時間の管理と計算を行う関数
const countUp = () => {
  timerId = setTimeout(() => {
    elapsedTime = Date.now() - startTime + timeToAdd;
    updateTimeText();
    countUp();
  }, 10);
};

start.addEventListener("click", () => {
  startTime = Date.now();
  countUp();
});

stop.addEventListener("click", () => {
  clearTimeout(timerId);
  timeToAdd += Date.now() - startTime;
});

reset.addEventListener("click", () => {
  elapsedTime = 0;
  timeToAdd = 0;
  // 00:00:000 を表示
  updateTimeText();
});

ポイント

const,let

定数は const ,変数は let を使います.

変数の意図しない変更を防ぐことができます.また,スコープが自身が定義されたブロックとなります.

アロー関数

毎度 function と書くのは面倒です. () => {} というように書くと見た目がすっきりします.

今回は関係ありませんが,this の扱いが少し違うので注意が必要です.

https://azu.github.io/what-is-this/

テンプレート文字列

"string text " + expression + " string text" は, `string text ${expression} string text` というように書くことができます.テンプレート文字列を使うことによってダブルクオートやシングルクオートを使う回数を減らすことができ,可読性が上がります.

デバッグ

上のコードにはバグがあるので修正します.

スタートボタンの連続クリック

このアプリケーションでは,連続で2回クリックをしたとき2つのタイマーイベント( countUp )が発生してしまいます.

この問題を解決する方法としていくつかのアプローチがあります.

  1. スタートボタンとストップボタンを統合する
  2. 連続で押せないように無効化する

一般的な(物理)ストップウォッチでは,1の選択肢が取られています.

ただ,手法としては2もあるのでどちらも紹介します.

スタートボタンとストップボタンを統合する

HTML

HTMLはスタートボタンとストップボタンが統合されたことにより,buttonタグが一つ減ります.

stopwatch.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>StopWatch</title>
  </head>
  <body>
    <div id="timer">00:00:000</div>
    <button id="start-stop">start</button> <button id="reset">reset</button>
    <script src="js/main.js"></script>
  </body>
</html>

JavaScript

現在の状態が動作中か停止中かを条件分けして,ボタンの処理を変える必要があります.

main.js
const timer = document.getElementById("timer");
const startStop = document.getElementById("start-stop");
const reset = document.getElementById("reset");

// 経過時間を保存する変数(単位:ミリ秒)
let elapsedTime;
// スタートボタンを押したときのUnix Epoch
let startTime;
// タイマーのID
let timerId = null;
// 以前 stop したタイミングまでの計測時間
let timeToAdd = 0;

// 表示される内容をアップデートする関数
const updateTimeText = () => {
  // 1分 = 1000 ミリ秒 * 60秒
  let m = Math.floor(elapsedTime / (1000 * 60));
  // 1分に満たなかったミリ秒のうち,秒となったもの
  let s = Math.floor((elapsedTime % (1000 * 60)) / 1000);
  // 1秒になれなかったもの
  let ms = elapsedTime % 1000;

  // ゼロパディング
  m = `0${m}`.slice(-2);
  s = `0${s}`.slice(-2);
  ms = `00${ms}`.slice(-3);

  timer.textContent = `${m}:${s}:${ms}`;
};

// 経過時間の管理と計算を行う関数
const countUp = () => {
  timerId = setTimeout(() => {
    elapsedTime = Date.now() - startTime + timeToAdd;
    updateTimeText();
    countUp();
  }, 10);
};

startStop.addEventListener("click", () => {
  // タイマーIDが停止中
  if (timerId === null) {
    startTime = Date.now();
    countUp();
    // ボタンのテキストをstartからstopに更新
    startStop.textContent = "stop";
  } else {
    // タイマーが動作中
    clearTimeout(timerId);
    timerId = null;
    timeToAdd += Date.now() - startTime;
    // ボタンのテキストをstopからstartに更新
    startStop.textContent = "start";
  }
});

reset.addEventListener("click", () => {
  elapsedTime = 0;
  timeToAdd = 0;
  // 00:00:000 を表示
  updateTimeText();
});

連続で押せないように無効化する

button タグには,disabled という属性を付けることができます.
JavaScript から disabled 属性をコントロールすることによってボタンを無効化します.

HTML

はじめのHTMLと同じコードですが,初期状態としてstopボタンを無効化しておきます.

stopwatch.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>StopWatch</title>
  </head>
  <body>
    <div id="timer">00:00:000</div>
    <button id="start">start</button> <button id="stop" disabled>stop</button>
    <button id="reset">reset</button>
    <script src="js/main.js"></script>
  </body>
</html>

JavaScript

はじめのコードに ボタンを有効化/無効化するコードを追加します.

main.js
const timer = document.getElementById("timer");
const start = document.getElementById("start");
const stop = document.getElementById("stop");
const reset = document.getElementById("reset");

// 経過時間を保存する変数(単位:ミリ秒)
let elapsedTime;
// スタートボタンを押したときのUnix Epoch
let startTime;
// タイマーのID
let timerId;
// 以前 stop したタイミングまでの計測時間
let timeToAdd = 0;

// 表示される内容をアップデートする関数
const updateTimeText = () => {
  // 1分 = 1000 ミリ秒 * 60秒
  let m = Math.floor(elapsedTime / (1000 * 60));
  // 1分に満たなかったミリ秒のうち,秒となったもの
  let s = Math.floor((elapsedTime % (1000 * 60)) / 1000);
  // 1秒になれなかったもの
  let ms = elapsedTime % 1000;

  // ゼロパディング
  m = `0${m}`.slice(-2);
  s = `0${s}`.slice(-2);
  ms = `00${ms}`.slice(-3);

  timer.textContent = `${m}:${s}:${ms}`;
};

// 経過時間の管理と計算を行う関数
const countUp = () => {
  timerId = setTimeout(() => {
    elapsedTime = Date.now() - startTime + timeToAdd;
    updateTimeText();
    countUp();
  }, 10);
};

start.addEventListener("click", () => {
  startTime = Date.now();
  countUp();
  // スタートボタンを無効化
  start.disabled = true;
  // ストップボタンを有効化
  stop.disabled = false;
});

stop.addEventListener("click", () => {
  clearTimeout(timerId);
  timeToAdd += Date.now() - startTime;
  // スタートボタンを有効化
  start.disabled = false;
  // ストップボタンを無効化
  stop.disabled = true;
});

reset.addEventListener("click", () => {
  elapsedTime = 0;
  timeToAdd = 0;
  // 00:00:000 を表示
  updateTimeText();
});

まとめ

プログラムは自分の書いたようにしか動きません.
プログラムを書くにはまず必要な機能を考え言語化し,そのとおりにプログラムを書くと動きます.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

lxmlメモ

はじめに

今回の内容はスクレイピングする上で用いたとあるライブラリについての個人的感想です。
どんなことができるのか、使ってみた感想をつらつらと書いていきます。

スクレイピングとは

Web上のページから情報を引き抜くこと。機械学習など大量のデータが欲しい時に、データの取得をプログラミングで自動化できるのでとても便利なものである。

lxmlとは

スクレイピングを実現するためのPythonモジュールのひとつ。他にもhtmllibやBeautiful Soupなどがある。

特徴

  • とにかく高速、おそらくモジュールがCを使って書かれたため?
  • HTMLの要素をDOM(Document Object Model)構造に変換する
  • XPath(次で説明)というものを使って抜き取る要素を指定

使い方

lxmlで要素を抜き取りたいときは、XPathというもので要素の場所を指定する必要がある。
XPathの使い方はこちらのものを引用しつつ説明する。

XPath(XML Path Language)とは、XML形式の文書から、特定の部分を指定して抽出するための簡潔な構文(言語)です。HTML形式の文書にも対応します。
CSSではセレクタを使ってHTML文書内の特定の部分を抽出しますが、XPathはより簡潔かつ柔軟に指定ができるとされています。

例としてbody以下のリンク要素(hogeクラス)を取り出す書き方を挙げる。

  • CSSセレクタ: html > body a.hoge
  • XPath: /html/body//a[@class="hoge"] または //a[@class="hoge"]

このようにXPathは要素を階層的に指定できる。
さらにXPathでは絶対パス形式と相対パス形式で要素を指定できるので柔軟に要素を抜き取ることができる。

具体例として以下のようなHTMLファイルから要素を抜き取るとする。

sample.html
<html>
<head>
    <title>sample page</title>
</head>
<body>
    <div class="test1">
        <h1>Hello World!!</h1>
    </div>
    <div class="table">
        <table border="1">
          <tr>
            <th>名前</th>
            <th>一言</th>
          </tr>
          <tr>
            <td>サンプル1</td>
            <td>よろしく</td>
          </tr>
          <tr>
            <td>サンプル2</td>
            <td>はじめまして</td>
          </tr>
                    <tr>
            <td>サンプル3</td>
            <td>これはテストです</td>
          </tr>
        </table>
    </div>
</body>
</html>

このとき、テーブル内の一言を抽出したい場合は、次のようにXPathを指定する。

//table/tr/td[2]

もしサンプル2が発している「はじめまして」を抽出するなら//td[4]と指定しても良い。
ただし、tdの数の変動を考慮するならば次のように指定する。

//table//td[contains(*,'サンプル2')]/following-sibling::td[1]

こうすることで中身がサンプル2であるtdの後ろにある要素(今回はtd)を抽出することができる。
XPathにはこうした細かい条件を指定できる関数もあるので、非常に便利である。
ここまでをコードとしてまとめる。

import lxml.html
import requests                        # Webページのソースを取得するのに用いる。今回はこれについての説明は省く

url = 'http://example.com/'            # URLの一例、今回は上のsample.htmlを指すものとする

r = requests.get(url)                  # URLにあるページのソースを取得
html = lxml.html.fromstring(r.text)    # 取得したソースをHtmlElementオブジェクトに変換

td = html.xpath("//table//td[contains(*,'サンプル2')]/following-sibling::td[1]")
# >>> print(td)
# '<td>はじめまして</td>'

指定された要素の中身(ex.文字列やURL)を抽出するには以下の文を最後に追加する。

text = td.text()

ただし、もしtdの中身がはじめまして<br />僕はサンプル2ですの場合、text()ははじめましてしか抽出できない。そういったときは次のようなメソッドを使う。

text = td.text_content()

こうするとtextにははじめまして僕の名前はサンプル2ですが代入され、要素内の全文字列を取得することができる。
その他様々なデータの抽出方法はこちらを参考に

感想

大量のデータをとるときでもわずか数分で取得できたのは神だった。
スクレイピングする際は「とりあえずlxml」の気持ちで生きていきたい。

参考文献

XPathのまとめ、要素の参照方法いろいろ
Python(lxml)でhtmlを処理する まとめ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

loadしたhtmlにjsを適用させる

loadしたhtmlにjsを適用させる

前提:フォルダ階層

階層
┣ static
┃ ┣ css
┃ ┃ ┗ menu.css
┃ ┣ js
┃ ┃ ┣common.js
┃ ┃ ┗menu.js
┃ ┣ html
┃ ┃ ┗ menu.html
┗ templates
  ┗ main.html

やりたかったこと

機能

現在いるページと対応するメニューの色を変更させたい。

コード

htmlで複数ページを作る時に、メニューのところのhtmlが長いのでmenu.htmlとして外部ファイルを作った。
それをcommon.jsで読み込み、main.htmlにmenu.htmlを適用させた。
更に今いるページと対応するメニューの色を変更するために
特定のタグをactiveにする処理を書いたmenu.jsをmenu.htmlに適応させたい。

main.html
<body>
    <div id="menu"></div>

    <!--jQueryの読み込みとか-->
    <script type="text/javascript"
        src="/webjars/jquery/3.3.1/jquery.min.js" charset="utf-8">  
    </script>
    <!--javaScriptの読み込み-->
    <script type="text/javascript" src="/js/common.js"></script>
    <script type="text/javascript" src="/js/menu.js"></script>
</body>
menu.html
<ul id="nav"> 
   <li><a href="/menu1">MENU1</a></li> 
   <li><a href="/menu2">MENU2</a></li> 
   <li><a href="/menu3">MENU3</a></li> 
   <li><a href="/menu4">MENU4</a></li> 
 </ul> 
common.js
$(window).on('load', function() {
      //main.htmlのmenu要素についてmenu.htmlを適用させる
    $("#menu").load('/html/menu.html');
});
menu.js
//現在のページのurlとメニューのurlが同じだったら、そこのタグをactiveにする
$(function() {
    $('#nav li a').each(function() {
        var $href = $(this).attr('href');
        if (location.href.match($href)) {
            $(this).addClass('active');
        } else {
            $(this).removeClass('active');
        }
    });
});
menu.css
/*activeになってる要素navの[li a]タグの文字を赤にする*/
#nav li a.active {
    color: red;
}

問題1

menu.jsはちゃんと読み込まれているっぽいけど、activeにならない。
コンソール上のエラーは発生してないが、なんかうまく適用されていない気がする

実際にやってみたこと

menu.html上で直接jsを呼んでみる

menu.html
<!--変更したところ↓-->
<script type="text/javascript" src="/js/menu.js"></script>
<!--変更したところ↑-->
<ul id="nav"> 
   <li><a href="/menu1">MENU1</a></li> 
   <li><a href="/menu2">MENU2</a></li> 
   <li><a href="/menu3">MENU3</a></li> 
   <li><a href="/menu4">MENU4</a></li> 
 </ul> 

問題2

とりあえず、jsは読み込まれて適用された。が、
コンソールに以下のエラー

error
[[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.

よくわらかないけどエラーが出るってことはダメな気がする。気持ち悪いし
あと、ググったらエンドユーザーに影響がどうのって書いてたらかダメなんだと思う

解決方法

menu.htmlをロードする時に一緒にjsもロードしてあげる!

common.js
$(window).on('load', function() {
      //main.htmlのmenu要素についてmenu.htmlを適用させる
    $("#menu").load('/html/menu.html');
    //変更したところ↓
    $.getScript("/js/menu.js", function() {
    });
    //変更したところ↑
});

これでエラーなくちゃんとactiveにすることができた

参考にしたサイト

http://mugen00.moo.jp/web/jquery/getscript-2

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む