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

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

ただいまの
回答率

89.99%

clickイベントをclickがなかったときも発火させたい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 2,884

wkh0413

score 18

前提・実現したいこと

Androidでは、clickイベントはユーザーのアクションがないと発火しないようですが、clickされなかったときも発火させたいです。擬似的にclickがあったということにできる方法はないでしょうか。

発生している問題・エラーメッセージ

動画を2つ連続で再生させたいのですが、動画を再生するときに、play()を使っています。
clickが無くても1つ目の動画が終了したら、2つ目の動画を再生したいです。
しかし、Androidで実行したときにデバッグしてみたら、play()はユーザーのアクションがないと実行されないということでした。

DOMException: play() can only be initiated by a user gesture.

該当のソースコード

var Video = function(url) {
  var thisObj = this;
  this.url = url;
  this.video = $('<video>').hide();
  this.video.attr('src',url);
};

Video.prototype.loadVideo = function(){
  this.video.get(0).load();
};

Video.prototype.playVideo = function(){
  this.video.get(0).play();
};

試したこと

jQueryの.trigger()を使ってみましたが、同様のエラーが表示されました。
clickイベントを無理矢理発火させる方向で考えていますが、それ以外に良い方法があれば教えていただけるとありがたいです。

補足情報(言語/FW/ツール等のバージョンなど)

Videoは動画を再生させるための変数です。
Javascript、HTML5を使用しています。
videoタグを使って再生させています。
ブラウザはChromeを使用しています。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • asahina_dev

    2016/07/26 16:49

    ブラウザのセキュリティを回避する方法は基本ない(あっても次期バージョンアップで塞がれる)ので リロード+オートプレイ等で擬似的に対応したほうがいいです。

    キャンセル

  • wkh0413

    2016/07/26 17:22

    ありがとうございます。
    やはり、自動再生をするのは難しいのですね。
    リロードすると、その先に実現したいことができないので、別の方法を探してみます。

    キャンセル

  • glapd332

    2016/07/31 20:43 編集

    連続再生、ということは、例えばa,bの動画があったとして、
    aの再生後にbを再生する、ということでよろしいでしょうか。

    キャンセル

  • wkh0413

    2016/07/31 21:03

    glapd332さんのおっしゃる通り、連続再生は、a,bの動画があったとき、aを再生終了後bを再生させることです。
    また、aの動画はユーザーが画面(動画領域)をクリックすることで.play()を実行させています。

    情報が不足していて申し訳ありません。

    キャンセル

回答 1

checkベストアンサー

+3

PC版GoogleChromeで確認しました。
この実装は強引?です。ただ疑似化とかはしてません。
単にイベント伝番を使っているだけです。

まず初めにコードを載せておきます。

var addListener = document.body.addEventListener ?
  function (target, eventType, handler) {
    target.addEventListener(eventType, handler, false);
  } :
  function (target, eventType, handler) {
    target.attachEvent('on'+eventType, handler);
  };

var removeListener = document.body.removeEventListener ?
  function (target, eventType, handler) {
    target.removeEventListener(eventType, handler, false);
  } :
  function (target, eventType, handler) {
    target.detachEvent('on'+eventType, handler);
  };

/**
 * ビデオクラス
 * @param url 動画のurl
 * @author glapd332
 * @version 2016.07.31
 */
var Video = (function() {
  function Video(url) {
    this.url = null;
    this.video = null;
    this.initialize(url);
  };

  Video.prototype.initialize = function(url) {
    var video = document.createElement('video');
    video.src = url;
    this.video = video;
    this.url = url;
    // 直接<body>に追加しています。
    // もしほかの要素に追加したいなら、
    // 要素(document.getElement系で取得したもの).appendChild(this.video);
    // としてください。
    document.body.appendChild(this.video);
  };

  Video.prototype.play = function() {
    this.video.play();
  };

  Video.prototype.pause = function() {
    this.video.pause();
  };

  Video.prototype.stop = function() {
    this.video.pause();
    this.video.currentTime = 0;
  };

  /**
   * @param eventType イベントタイプ(例)load, clickなど
   * @param handler イベント発火時に伴う処理
   */
  Video.prototype.setEvent = function(eventType, handler) {
    this.video.addListener(eventType, handler, false);
  };

  return Video;
}());

var video1 = new Video("mv/sample.mp4");
var video2 = new Video("mv/sample.mp4");
var isVideo1Running = false;

video1.setEvent('click', function() {
  if ( isVideo1Running === false ) {
    video1.play();
    isVideo1Running = true;
    return;
  };
  video1.pause();
  isVideo1Running = false;
});

video1.setEvent('loadeddata', function() {
  video1.play();
  isVideo1Running = true;
});

video1.setEvent('ended', function() {
  video2.play();
});

解説

簡単にまとめると、今回必要なことは、

  1. 自動再生
  2. 連続再生
    の2つです。
    それぞれ、videoのイベントハンドラで表現できます。

クロスブラウザ対策

まず初めに、今回はイベントを多用するのでクロスブラウザを意識して、

var addListener = document.body.addEventListener ?
  function (target, eventType, handler) {
    target.addEventListener(eventType, handler, false);
  } :
  function (target, eventType, handler) {
    target.attachEvent('on'+eventType, handler);
  };

var removeListener = document.body.removeEventListener ?
  function (target, eventType, handler) {
    target.removeEventListener(eventType, handler, false);
  } :
  function (target, eventType, handler) {
    target.detachEvent('on'+eventType, handler);
  };


と記述します。
これは、IEとその他ブラウザでのイベントの設定の仕方が異なるのを吸収するためのものです。
あえて三項演算子で戻している理由としては、何度も
"addEventListenerが使えるかな?それともattachEventかな?"
と調べると、結果的にDOMに何度もアクセスすることになり、
パフォーマンスが落ちるのでそれを防ぐためです。

Videoクラス

var Video = (function() {
  function Video(url) {
    this.url = null;
    this.video = null;
    this.initialize(url);
  };

  Video.prototype.initialize = function(url) {
    var video = document.createElement('video');
    video.src = url;
    this.video = video;
    this.url = url;
    document.body.appendChild(this.video);
  };

  Video.prototype.play = function() {
    this.video.play();
  };

  Video.prototype.pause = function() {
    this.video.pause();
  };

  Video.prototype.stop = function() {
    this.video.pause();
    this.video.currentTime = 0;
  };

  Video.prototype.setEvent = function(eventType, handler) {
    this.video.addListener(eventType, handler, false);
  };

  return Video;
}());


単に拡張性に優れていること、すっきりとする、という理由から使用しています。
(本来はDOMと処理を切り離すべきですが...)

document.body.appendChild(this.video);


直接bodyに追加していますので、bodyでなく
あるdivにしたい場合は書き換えてください。

処理

var video1 = new Video("mv/sample.mp4");
var video2 = new Video("mv/sample.mp4");
var isVideo1Running = false;

video1.setEvent('click', function() {
  if ( isVideo1Running === false ) {
    video1.play();
    isVideo1Running = true;
    return;
  };
  video1.pause();
  isVideo1Running = false;
});

video1.setEvent('loadeddata', function() {
  video1.play();
  isVideo1Running = true;
});

video1.setEvent('ended', function() {
  video2.play();
});

まず動画を2つ、video1, video2用意します。

var video1 = new Video("mv/sample.mp4");
var video2 = new Video("mv/sample.mp4");


今回は、どちらも"mv/sample.mp4"であるとします。
また、

var isVideo1Running = false;


というフラグをたてておきます。
動画が再生されているかの判別用です。
trueなら再生、falseなら停止されているということを表現します。

処理>>再生/一時停止

video1.setEvent('click', function() {
  if ( isVideo1Running === false ) {
    video1.play();
    isVideo1Running = true;
    return;
  };
  video1.pause();
  isVideo1Running = false;
});


単に、一時停止と再生をするためです。
先ほどのフラグで判別します。
もちろんこちらをクリックして再生することもできます。
質問者様の現在の実行の仕方もできます。

処理>>自動実行

video1.setEvent('loadeddata', function() {
  video1.play();
  isVideo1Running = true;
});


自動実行のキモです。でも3行。クラスのおかげです。
ここで、自動実行します。
なぜできるのか?それは動画が読み込まれると同時にVideoクラスの.play()が自動発火されるからです。ではVideoクラスのvideo要素のplay()が実行されるのは?ということの答えは、
...すいません。よくわかりません。おそらくイベント伝番的な何かだと思います。
調べておきます。

処理>>連続再生

video1.setEvent('ended', function() {
  video2.play();
});


これは、video1の再生の終了後に発行される'ended'イベントを使っています。
これで連続再生が可能なはずです。

結論

以上でおそらく...実行できると思います。
一応GoogleChromeのスマホデバッグ機能で試して動くので良いと思って解答しましたが...、
なにせ私は携帯を持っていないので、実際には違った動きをするかもしれません。
なお、GoogleChromeのデバック機能のスマホの、
GalaxyS5, Nexus5s, Nexus6P, iPhone5, iPhone6, iPhone6Plus, iPad
での動作を確認しています。

追記

wkh0413様、私こそ、質問文をろくに読まずに質問をしてしまって申し訳ありません。
また、該当のソースコードをわざわざ書いていただいたのにもかかわらず、
別の書き方で実装してしまい、申し訳ありません。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/08/01 12:28

    glapd332様
    丁寧なご回答ありがとうございます。ロード終了を一つのアクションとしてplay()を実行させるという発想はありませんでした。大変勉強になります。また、クロスブラウザのことまで考慮できていなかったので、その点も非常に勉強になります。
    ソースコードですが、別の書き方で実装してくださったおかげで、新たに必要なこと(クロスブラウザなど)や、自分の書き方との差異についても知るきっかけになったので、むしろありがたいです。
    スマホで実装してみます!ありがとうございます!

    キャンセル

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

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