質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.11%

読上時計サンプルコードのエラー内容を教えてください

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 471

score 21

次のページをiOSで表示するとJavaScript実行中にエラーとなるようでコードが走りません。エラーメッセージを見られる方、内容を教えていただけないでしょうか。(MacかLinuxかWindows10があればWeb Inspectorでエラーメッセージを確認できるようですが、本件の為だけに導入するわけにはいかないのです)

該当のソースコード

<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>x117</title>
<script>
window.onload = function(){
    window.onbeforeunload = function(){
        speechSynthesis.cancel();
  }
}
</script>
</head>
<body>
読上時計 - <span id="rcstat"></span><br>
<button id="start-btn">開始</button>
<button id="stop-btn">停止</button>
<hr>
設定
<pre>
間隔:<select id="term">
<option value="10">10秒</option>
<option value="300">5分</option>
</select>
 声:<select id="voice-select"></select>
</pre>
<pre>
pitch <input type="range" id="sld_pitch" value="1.0" min="0.0" max="2.0" step="0.1"  style="width:300px"onchange="ValSetp()"><span id="vpitch"></span>
rate  <input type="range" id="sld_rate" value="1.0" min="0.1" max="10.0" step="0.1"  style="width:300px"onchange="ValSetr()"><span id="vrate"></span>
volume<input type="range" id="sld_volume" value="1.0" min="0.0" max="1.0" step="0.05"  style="width:300px"onchange="ValSetv()"><span id="vvolume"></span>
</pre>
<hr>
<div style="background-color:#FED;">
Debug
<hr style="border-top: 1px dotted; border-bottom: 0px;">
SpeechSynthesis API 対応確認
<pre><script>
  if ('speechSynthesis' in window) {
    document.write("。")
  } else {
    document.write("このブラウザは音声合成に対応していません。")
  }
</script></pre>
<hr style="border: 1px dotted #ddf;">
<pre>
MainLoop / uttrsetting
実行時刻
<span id="nowtime"></span>
次回読上開始可能時刻
<span id="utterabletime"></span>
次回読上
<span id="utterstrtime"></span>
<textarea id="utext">
アドベントカレンダーは、クリスマスまでの日数を数えるためのカレンダー。
</textarea></pre>
発話テスト ※モバイルOSでは一度はユーザが操作しないと音が出ない?<br>
<button id="speak-btn">再生</button>
<button id="cancel-btn">停止</button>
<hr style="border: 1px dotted #ddf;">
SpeechSynthesis - property<br>
取得時刻:<span id="ssptime"></span>
<pre id="ssp"></pre>
<hr style="border: 1px dotted #ddf;">
SpeechSynthesisUtterance - event<br>
start時刻 / startからの経過時刻[秒・ms]
<pre>
start:<span id="ssues">-</span>
  end:<span id="ssuee">-</span>
error:<span id="ssueerr">-</span>
</pre>
</div>
<script>
function date2str(et) {
    //et: elapsed time from unix epoch
    return new Date(et).toDateString()
        + " " + new Date(et).toTimeString();
}

function fgetssp() {
  bufstr = '.paused: ' + speechSynthesis.paused +'\n';
  bufstr += '.pending: ' + speechSynthesis.pending +'\n';
  bufstr += '.speaking: ' + speechSynthesis.speaking +'\n';
  document.getElementById('ssp').textContent = bufstr;

    //showtime
    document.getElementById('ssptime').textContent = date2str(Date.now());

  setTimeout(fgetssp, 750);
}

fgetssp();
</script>
<hr>
<xmp>
参考・引用・流用元

https://mogya.com/2012/01/windows7text_to_speech.html
Windows7で音声合成(Text to speech) – もぎゃんらんど

http://www.neko.ne.jp/~freewing/software/ms_sapi_tts/
日本語や中国語等の音声合成(SAPI TTS)を無料で使う方法 (Speech Platform Server Runtimeを裏技で SAPI 5.1として動作させる方法)

http://www.createsystem.co.jp/DTalkerSapi1.html
製品情報 ドキュメントトーカ 日本語音声合成エンジン for Windows
ドキュメントトーカ 日本語音声合成エンジンは、Microsoft社のSpeech API Ver4(SAPI4)及び Ver5(SAPI5) に対応した音声合成エンジンです。
┃
┗ドキュメントトーカ Plus V2.1
 AquesTalkを、ドキュメントトーカ日本語音声合成エンジン(AquesTalk版)として、フリーで提供

https://qiita.com/hmmrjn/items/be29c62ba4e4a02d305c
Webページでブラウザの音声合成機能を使おう - Web Speech API Speech Synthesis - Qiita
┃ 主にこちらから
┣https://codepen.io/rodhamjun/pen/BGvrqr
┃Web Speech API SpeechSynthesis Demo by rodhamjun = hmmrjn
┃ 完成度の高いサンプル
┣https://jsfiddle.net/hmmrjn/o68vgqh0/1/
┃対応確認
┃
┗https://jsfiddle.net/hmmrjn/u1y4aL9v/
 再生停止一時停止再開

https://www.nict.go.jp/JST/JST5.html
https://www.javadrive.jp/javascript/date_class/index1.html
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date
https://web-designer.cman.jp/html_ref/abc_list/input_range/
https://w3c.github.io/speech-api/speechapi.html#speechsynthesisevent
https://app.codegrid.net/entry/2016-web-speech-api-1
Web Speech APIの実装 - Speech Synthesis API | CodeGrid
 一時停止状態で画面をリロードすると音が出なくなる
https://coliss.com/articles/build-websites/operation/css/css-snippets-for-horizontal-rules.html
http://www.htmq.com/html/select.shtml
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10102376495
https://www.google.com/search?q=ScriptProcessor
</xmp>
<script>
function ValSetp(){
    document.getElementById("vpitch").innerHTML=document.getElementById("sld_pitch").value;
}
function ValSetr(){
    document.getElementById("vrate").innerHTML=document.getElementById("sld_rate").value;
}
function ValSetv(){
    document.getElementById("vvolume").innerHTML=document.getElementById("sld_volume").value;
}
ValSetp();
ValSetr();
ValSetv();

  const utext      = document.querySelector('#utext')
  const voiceSelect = document.querySelector('#voice-select')
  const speakBtn  = document.querySelector('#speak-btn')
  const cancelBtn = document.querySelector('#cancel-btn')
  // selectタグの中身を声の名前が入ったoptionタグで埋める
  function appendVoices() {
    // ① 使える声の配列を取得
    // 配列の中身は SpeechSynthesisVoice オブジェクト
    const voices = speechSynthesis.getVoices()
    voiceSelect.innerHTML = ''
//    voices.forEach(function(voice) { //ES6非使用版
    voices.forEach(voice => { // アロー関数 (ES6)
      // 日本語と英語以外の声は選択肢に追加しない。
      if(!voice.lang.match('ja|en-US')) return
      const option = document.createElement('option')
      option.value = voice.name
//      option.text  = voice.name +'(' + voice.lang +')' //ES6非使用版
      option.text  = `${voice.name} (${voice.lang})` // テンプレートリテラル (ES6)
      option.setAttribute('selected', voice.default)
      voiceSelect.appendChild(option)
    });
  }

  appendVoices();

  // ② 使える声が追加されたときに着火するイベントハンドラ。
  // Chrome は非同期に(一個ずつ)声を読み込むため必要。
  speechSynthesis.onvoiceschanged = e => {
    appendVoices()
  }

    function utter() {
    // 発言を作成
    const uttr = new SpeechSynthesisUtterance(utext.value)

    // ③ 選択された声を指定
    uttr.voice = speechSynthesis
      .getVoices()
      .filter(voice => voice.name === voiceSelect.value)[0]
        // 音程・速さ・音量を設定
        uttr.pitch = document.getElementById("sld_pitch").value
        uttr.rate  = document.getElementById("sld_rate").value
        uttr.volume = document.getElementById("sld_volume").value
        //イベント設定 debug/
        uttr.onstart = function(event) { document.getElementById('ssues').textContent = date2str(Date.now()); }
        uttr.onend = function(event) { document.getElementById('ssuee').textContent = event.elapsedTime; }
        uttr.onerror = function(event) { document.getElementById('ssueerr').textContent = event.elapsedTime; }
        // /debug

    // 発言を再生 (発言キュー発言に追加)
    speechSynthesis.speak(uttr)
  }
  speakBtn.addEventListener('click', function() {
    utter()
  })
  cancelBtn.addEventListener('click', function() {
    // 再生停止 (発言キューをクリアして止まる)
    speechSynthesis.cancel()
  })
</script>
<script>
  const startBtn  = document.querySelector('#start-btn')
  const stopBtn = document.querySelector('#stop-btn')
    const rcstatNamedTag = document.getElementById('rcstat')

    let state_run = false;
    let offset, utterterm;
    let et_now, et_utterstr, et_utterable = 0;

    function uttrsetting() {
        let str, nmin, nsec;

        offset = 0; //[ms] 発話開始を前倒し(0ならその時刻になってから発話することになる)
        utterterm = 1000 * document.getElementById("term").value;

        et_now = Date.now(); //elapsed time from unix epoch
        // 発話内容の時刻
        et_utterstr = utterterm * Math.ceil((et_now + offset) / utterterm);
        // 発話開始時刻
        et_utterable = et_utterstr - offset;
    // dbg
    document.getElementById("nowtime").textContent = et_now
        + " " + date2str(et_now);
    document.getElementById("utterabletime").textContent = et_utterable
        + " " + date2str(et_utterable);
    document.getElementById("utterstrtime").textContent = et_utterstr
        + " " + date2str(et_utterstr);

        //発話文生成
        d = new Date(et_utterstr);
        nmin = d.getMinutes();
        nsec = d.getSeconds();
        str = d.getHours() + "時";
        if (!((nmin == 0) && (nsec == 0))) {
            str += nmin + "分";
        }
        if (nsec != 0) {
            str+= nsec + "秒";
        }
        str += "です。";
        utext.value = str;
    }
    //uttrsetting(); // dbg

    function uhrMainLoop() {
        et_now = Date.now();
        // dbg
        document.getElementById("nowtime").textContent = et_now
            + " " + date2str(et_now);

        if (et_utterable <= et_now) {
            speechSynthesis.cancel();
            utter();
            uttrsetting();
        }

        if (state_run) {
            setTimeout(uhrMainLoop, 1000);
        } else {
            rcstatNamedTag.textContent = "停止中";
        }
    }

  startBtn.addEventListener('click', function() {
        state_run = true;
        rcstatNamedTag.textContent = "作動中";
        uttrsetting();
    uhrMainLoop()
  })
  stopBtn.addEventListener('click', function() {
    state_run = false;
  })
</script>

</body>
</html>

試したこと

document.write文挿入と</script><script>行の挿入による不動作となるコード範囲の限定で、149行~169行、
const voiceSelect = document.querySelector('#voice-select')
からappendVoices関数の終わりまでに問題があることは分かりました。

補足情報

  • Windows 7 + Firefox では動作します。
  • iPad & iOS 10.3では動きません。
    Safari, Firefox, Edge(Windows機上のsHTTPd(悪魔猫将軍さんのほう)で配信)、Documentsアプリ内ブラウザ(smb経由で取得したファイルをWebView?で表示)いずれも同様です。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

やや古い情報ではありますが、こちらが参考になるかと思います。
[http://stfstfstf.blogspot.com/2013/06/iphoneconsolelog.html]

<以下引用>
1.お手軽な方法

windowのonerrorイベントを拾って、エラーの内容をそのままalert表示してしまうという解決方法。
下記の三行を追加するだけで実装できる。

window.onerror = function(error) {
alert(error);
};

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/29 18:50

    ありがとうございます。おかげさまでエラー内容を取得できました。
    特に問題となっていたのは以下でした。
    SyntaxError: Can't crate duplicate variable that shadows a global property: 'utext'
    iOSのSafariでは変数名とidが同じであってはならないとのことでしたので、名前を変更しました。これで動くようになりました。
    エラーはもう一つあり、以下でした。
    TypeError: undefined is not an object (evaluating 'document.getElementsByTagName("div")[1].innerHTML')
    こちらは意味・原因が分からず継続調査中です。(divタグを取り除いても解消しない)

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る