ナム山

家最高 今年一年でサックスが吹けるようになるか観測中

firefoxで動画が再生されない - 変数の巻き上げと処理順序 -

症状

Firefoxでのみ、JavaScriptからprependしているvideoタグが自動再生されない。

原因

  • videoタグ自体の不具合でなく、自動再生の関数内で変数がundefinedになる。
  • 自動再生処理がload終了後にちゃんと走っていなかった。

処理順序は以下
ready内でvideoタグをprepend、その後load内で自動再生の関数
このように切り分けていたが、ページ遷移してきた時に処理順序が担保されていなかった。(先に自動再生の関数が叩かれていた)
後述の背景でブラウザごとに処理を分けたせいではなく、そもそも描画と再生段階でエラーを吐いているようだったので原因は以下だと切り分けて進めた。

  • コールバック処理しきれていなかった
  • さらに変数の巻き上げ

背景と仕様

以前、Chromeのポリシーの変更の際videoタグで自動再生が動かなくなった。
案件の内容に関わるので詳細は省くが、さらにChromeとそれ以外で分けたい処理があったので条件分岐していた。

  1. 「ブラウザ」「UA」で処理を分岐させる
  2. jsで条件にあったvideoタグを書き出す

以上が仕様。
ソース上にvideoタグがないため、描画→自動再生、と順序だって処理させる必要があった。
以前までは全ブラウザ問題なく動いているようだったが今回firefoxでうまく動かなかった。

対策

  • 関数化して処理順序を担保した
  • グローバル関数化して巻き上げ対策した

コード

■旧js

$(function() {
	if(Chromeだった場合 かつ PC幅だった場合) {
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//本当はブラウザ別の処理が書いてある
    }elseif(Chrome以外 かつ PC幅だった場合){
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//こちらもブラウザ別の処理が書いてある
    }
});

$(window).on('load',function(){
    if( PC幅 ){
        var v = $('#video').get(0);
        v.play();
        //さらにオプションなど
    }
});

■新js

$(function() {
	if(Chromeだった場合 かつ PC幅だった場合) {
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//本当はブラウザ別の処理が書いてある
		videoStart();//関数化した
    }elseif(Chrome以外 かつ PC幅だった場合){
		$('#player').prepend('<video id="video" src="movie01.mp4" controls muted loop preload="auto"></video>');
		//こちらもブラウザ別の処理が書いてある
		videoStart();//関数化した
    }
});

function videoStart(){
    if( w >= 768 && flg == 'off' && ua.indexOf('iPad') == -1 ){
        v = $('#video').get(0); //グローバルに
        v.play();
    }
    return this;
}
巻き上げとコールバック

エラーは大きく2つ

  • 変数が未定義と怒られる
  • prependされる前に動画を再生しようとして行方不明になる

関数化してコールバック
先にコールバックについて、function(ready)とloadで分けていたので基本的には担保されていたはず。
しかし時と場合によっては順次処理がうまくいかず先に自動再生のブロックまで進んでしまいエラーになるようだった。
そのため関数化して明確にロード処理の終わりで叩かせることに。
巻き上げ
v = undefinded で怒られていた。
変数の巻き上げと言ってしまうと正確ではないが、宣言しているはずなのに未定義と怒られるケースから、変数の巻き上げ対策を取ることにした。
そのため根本的な解決ではないが、グローバル関数化したら未定義で怒られることはなくなった。
qiita.com

明示的に関数の先頭で宣言しようということでグローバル変数で宣言した。
(2019.11.25追記)今考えるとprependを飛ばしてエラーが起きていたので、単純に処理順序が担保されれば自ずと解決したかもしれない...。

備考と感想

  • 一応サンプルコードなのでvとか簡単にしてますが変数名はもうちょっとちゃんとしてます
  • 定数でもよかったかも

JavaScript特有の巻き上げと、処理タイミング、という一番ありがちなコンボだったので原因の切り分けとデバッグまで1時間もかからなかった。
jQueryも3系なのでloadとreadyで明示的に動いてくれていたのであぐらをかいていた。
関数化する方がスマートといえばスマートなので、これからは意識して綺麗なコードにしようと思った。

経過観測5〜マイナーチェンジ〜

振り返りと変化

このブログは自分用のメモと成長記録が主目的なので、定期的に目標の振り返りをしている。
2019年の目標はこちら。

  1. 英語 - 簡単な日常会話ができるようになる + 観たい映画を字幕なしで鑑賞できるようになる
  2. プログラミング - jQueryを極める + 他の言語にも手を出す
  3. ギター - 難しい課題曲を弾けるようになる + スイープの習得
  4. サックス - ライブで使えるくらいになる(初心者脱出)

4つの大目標に対して、具体的な内容は以下。

  • 10日区切りでクリアできる簡単な目標の設定(4分野 * 8項目)
  • 各分野、日々のルーティンも習慣化を目指す(4分野)

現状の進行具合

ほぼ毎日続いていること

・英語日記

一日置きくらいにはできてる

・プログラミングの学習(↑)

ちょっとサボりがち

・ギターの練習(↓)
・サックスの練習

目も当てられない

・特になし
ギターとプログラミングが入れ替わり。
案件が立て込んでいたので時間の都合上仕方ないかなと。
記事が増えたのはそれ故。

良かったこと、改善したポイント

英語は近頃リスニング中心に。一番弱いので聞き取れないことが多いけど、慣れてきた感じもあって楽しい。
サックスは口輪筋のトレーニングをルーティンにすることで音が出せなくても毎日取り入れるようにマイナーチェンジした。

分かったこと

サックスの基礎力
アンブシュア(くわえ方)が今の所の課題だとわかってきた。
長いこと吹いているとバテてしまうので、ルーティンへ口輪筋トレーニングも加えることにした。
音を出せるのが一番の練習だけど、これでひとまず毎日サックスに関わるトレーニングができそう。

変わったことと感想

11ヶ月経った感想。

  • 何かを習慣にするにはある程度の緩さも必要。
  • マイナーチェンジも全然OK。
  • 仕事の進め方も自律できるようになってきた。

習慣化のコツみたいな話。
前回と似ているけど、あんまり毎日やること自体に縛られると重荷になる。
せっかく4分野も手を出しているので、飽きてきたらその時々やる気が起きやすいものへ手を伸ばしている。
総量として無為に過ごすより何倍も良い。
さらにサックスのルーティンのように、都度こっちの方が効率的だと思ったらメニューはマイナーチェンジして全然問題ないとわかった。
あとは最近テクノを掘っているのでTwitterで初めて聴くアルバムを挙げているけど、ただ記録しているだけなのに以前より深く、広く聴ける気がして良い。
何でも記録してみると、振り返られるしむしろ振り返ることの方が自分の実になるのかもと思った。

PHPの配列に値を追加する(resister_post_typeのオプションをフックしてまとめて追加する)

前回、Wordpressでresister_post_typeを設定する内容を扱った。
カスタム投稿の一覧表示を作る - ナム山

この方法ならば、追加したい固定ページの登録内容がずらっと並ぶわけだが、似たようなオプションを書き連ねるのがスマートでないと感じた場合はオプションの配列を別にまとめてフックできるらしい。

add_filter('register_post_type_args', 'movies_to_films', 10, 2);
function movies_to_films($args, $post_type){
 
    if ($post_type == 'movies'){
        $args['rewrite']['slug'] = 'films';
    }
 
    return $args;
}

引用: 
https://developer.wordpress.org/reference/hooks/register_post_type_args/

rewriteの配列に'film'を別アクションから引っ掛けているわけだ。
ここで配列に追加する記法が面白かったので少し調べてみた

参考: https://uxmilk.jp/8880

register_post_typeのオプションのように、連想配列であれば
$targetArray['key'] = 'value';
という具合で追加できた。

<?php
$args = array('test' => '001', 'test02' => '002');
$args['test03'] = 'ccc';
var_dump($args);

//結果
array(3) {
  ["test"]=>
  string(3) "001"
  ["test02"]=>
  string(3) "002"
  ["test03"]=>
  string(3) "ccc"
}
?>

配列もそのまま追加できる

<?php $args['test03'] = array('ccc','ddd'); ?>

配列、ループ処理あたりを理解、使えるようになってくると一気に考え方が論理的になる感じがする。
PHPは便利だな〜

カスタム投稿の一覧表示を作る

Wordpressでカスタム投稿をよく利用するが、久しぶりに触った時にアーカイブ作成するのに手間取ったので備忘録。いい機会なのでリファレンスを再確認した。
具体的にはアーカイブを有効にして、アイキャッチを投稿できるようにしたい。

参考:https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/register_post_type

基本形

<?php register_post_type( $post_type, $args ); ?>

よく使うやり方

add_action( 'init', 'function名' );
function function名(){
    register_post_type( 'post名',//任意のpost名をつける
        array(
            'labels' => array( //ラベル(表示名)の細かなオプション,とりあえずnameがデフォルトで使用される
            'name' => __( 'ラベル名' )
        ),
        'menu_position' =>5,
        'public' => true,
        'exclude_from_search' => false,
        'hierarchical' => true,//階層構造 デフォルトはtrue
        'taxonomies' => [],
        'has_archive'  => true, 
        )
        'supports'     => array(
	        'title', //タイトルを表示
	        'editor', //カスタムフィールドを表示
	        'thumbnail' //アイキャッチを利用する
	    )
    );
}

もちろん先に$argsを決め込んでからregister_post_type( $post_type, $args );でシンプルに書いてしまっても良い。

他にもオプションはあるが内部の操作に必要そうなものをざっと並べた。
上から順に
・labels(配列) - ラベルの配列
・public(真偽値) - 管理画面とフロントエンドで公開するか否か
・exclude_from_search(真偽値) - 検索結果から除外するか否か
・hierarchical(真偽値) - 階層を持つかどうか 使用するなら'supports'パラメータに'page-attributes'を含めること
・taxonomies(配列) - タクソノミーの配列 register_taxonomy()で明示的に登録する
・has_archive(真偽値/文字列) - アーカイブを有効にする デフォルトは$post_type
・supports(配列) - add_post_type_support()呼び出しのエイリアス falseでデフォルト設定(title,editor)を止める

注意点

  • 必ずinitアクションの中から呼び出すこと
  • カスタムタクソノミーを付ける場合taxsonomies引数を使って登録する(フィルターを使う際紐づかなくなってしまう)
  • 同じくregister_taxonomy()を使って明示的に定義する必要がある

タクソノミー周りは複雑なのでプラグイン使いがち

今回の目玉はhas_archiveとsupports。
supportsの詳細はリファレンスを参照。
無事、'thumbnail'でアイキャッチをオンにしてアーカイブできるようになった。

post_typeの登録だけあって基本的な表示内容やオプションはここで指定できる。
改めてリファレンス読んだらこんなこともできるのかと発見があってよかった。
最低限の内容抑えるついでにこんなこともあるのね、程度に頭に入れておくといざ必要になった際に必要なもののアタリがつくので良いと思った。

git cherry-pickを使う

背景

先日、運用更新が行われているサイトのスポット作業中、ブランチで一括置換したファイルが更新案件と大量に被ってしまった。
ステージングサーバーは一つなので、全ての作業を取り込んだ状態でクライアントに確認してもらいたい。
ちょうど良い機会だったのでcherry-pickを挙動調査がてら使ってみた。

cherry-pickの機能

特定のコミットだけを他のブランチから反映することができるコマンド。

cherry-pickのコマンド

$ git checkout xxxx //取り込みたいコミットがあるブランチ
$ git log

//各コミットのIDが出てくる

$ git checkout yyyy //取り込み元のブランチに戻る
$ git cherry-pick [commit ID]

挙動の確認

むしろ常にこの状態が続くのであれば、マージ用のブランチを立てて管理しても良い。
実際クライアント確認時はマージ用のブランチに全てマージしてアップロードした。
しかし各ブランチの公開タイミングが読めない + 先立って該当の置換作業が公開されても支障ない内容。
ということから他のブランチへ自分の作業だけチェリーピックしておくことにした。
バキバキに更新した他の作業内容が含まれては困るし、心配性なので挙動のテストを行った。

・ファイルA、ファイルB 2つのファイルを用意する。
・1ファイルずつ手を加えたとき、後半のコミットを取り込んだ場合に「ファイルA」の状態が変化しなければOK
※下のブランチに、ピンクで囲んだコミットの内容をcherry-pickする。
f:id:numb_yam:20191030175303p:plain
これは感覚の問題だが「特定のコミットだけを反映する」と自分で書いておきながら「そのコミット時点での状態」が取り込まれないか確認しておかないと気持ち悪くて...。
結果はもちろん問題なし。上記イラストの通り、該当コミットの作業内容だけ反映された。
コミットとは「差分」のことであり、各コミットIDに記録されているのはコミット時点での「状態」ではなく、各コミットの「差分」でしかない。
というのを改めて理解できた。
cherry-pick自体は便利な機能だがこれが乱発するのはそもそも案件のハンドリングの問題でもあるので、いざというときだけにしておきたい。

JavaScriptのオブジェクトをimportして表示する

アプリケーションで生成された画像を、別ページでギャラリーのように表示するシステムの組み込みをした。
以前REST APIで生成されるJSONを取得するコードを書いたのでたかをくくっていたが、いざ組み込もうとしたらES6で表記されていて困ったのでメモ。
REST APIでWordpressの更新情報を別ドメインから取得する(JSONPを使用) - ナム山

取り込みたいjsファイルはこんな感じ

const target = [
  { url:'/app/#test?01', img: 'https://dammy.jp/img/img_01.jpg' },
  { url:'/app/#test?02', img: 'https://dammy.jp/img/img_02.jpg' },
  { url:'/app/#test?03', img: 'https://dammy.jp/img/img_03.jpg' },
  { url:'/app/#test?04', img: 'https://dammy.jp/img/img_04.jpg' },
  { url:'/app/#test?05', img: 'https://dammy.jp/img/img_05.jpg' },
  { url:'/app/#test?06', img: 'https://dammy.jp/img/img_06.jpg' },
  { url:'/app/#test?07', img: 'https://dammy.jp/img/img_07.jpg' },
  { url:'/app/#test?08', img: 'https://dammy.jp/img/img_08.jpg' },
  { url:'/app/#test?09', img: 'https://dammy.jp/img/img_09.jpg' },
  { url:'/app/#test?10', img: 'https://dammy.jp/img/img_10.jpg' }
]
export default target

内容は適当だけど、これを取り込み、クエリを持ったURLとパターン違いの画像をリストで表示する。
遷移先でクエリの通り処理して表示、といった案件。

JSONではないので以前のコードは使えないし、そもそもES6なのでconsoleで試しに吐き出してみることさえできない。
exportとimportのリファレンスを調べて出来上がったのがこちら。

import defaultExport from './test.js';//パスとファイル名で上記jsファイルを指定 

var html = '';
for(var i = 0;i<defaultExport.length;i++){
  var url = defaultExport[i].url;
  var img = defaultExport[i].img;
  html += `<li><a href="${link}"><img src="${img}"></a></li>`;
}
$('#dest').append(html);

export

読んで字のごとくデータを出力するもの。

  • 名前付きエクスポート (モジュール当たり0個以上のエクスポート)
  • デフォルトエクスポート (モジュール当たり1個のエクスポート)
  • 混合エクスポート

の3種類ある。

はじめ、defaultの後に識別名が付いていたので名前付きエクスポートだと早合点してしまい「そんなものない」と怒られてしまう。

参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export

import

ここを参考にtype="module"付きで取り込む。
デフォルトエクスポートかなと思い直し
>>import defaultExport from "module-name";<<
で取り込むと無事成功した。

参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import

ここを読むとpromiseで処理しても親切だったかもしれない。
今回重い腰を上げてようやくES6に触り始めた。
いい機会なので環境も含めてもっと取り入れようかなと思った。

プログラミングを勉強していてよかったこと1

自分は器用じゃない。
物事を保留して先に進めないので、100個問題があったら1から解いていく。
複雑な原因で物事が進行しないときは手が止まってしまう。
他にも効率からは程遠い行動を選びがちで、よく言えば欲求に素直だが、悪く言えば目移りしがちな人間だった。
当初の目標を忘れ、すぐに優先順位を変えてしまうのである。

今も本質的には変わらないが、プログラミングを学ぶことによって行動や思考方法が変わった点はある。
山積みの課題を見つけたときには、今では全体に目を通してからやり方を考えられるようになった。
何がどうなってダメになってるかわからない時も、一つずつ解決して行けば快方に向かうことも実感として理解できた。
少しずつ効率化することにも抵抗がなくなった。

効率化することに抵抗がある、と聞くと不思議に思う人が多いだろう。
自分でもよくわからないが、効率化することで「取りこぼしてしまうかもしれない何か」に対する漠然とした不安があったのだ。
一括置換で余分にコードを消してしまうのではないか。大量のマージコミットで先祖返りを起こしてしまうのではないか。
これらはひとえに経験不足、技術不足に起因するものだ。
経験を積めば未知への不安はなくなるし、自動化されたプログラムは中身を理解していないから怖いだけだ。
そもそもスコープを絞って、小さな単位の一括処理を重ねることでも十分効率化できる。
こうした技術を使わず、全て手作業にすることで得られる安心は、自分でブラックな作業を課しているに他ならない。

誰しも変化は怖いし、全自動化のような仕組みは学習コストもかかる。
それでも自分に合った粒度で良いので新しいワークフローを試してみたりすると、代わりに空いた時間で他のことが出来るようになった。
そういう分かりやすい対価を実際に得て、少しずつ恩恵に預かることを繰り返していくと次第に自信に変わって行った。
少しずつやり方を変えられるところは変えて行った。

そういう時、決まって今まで失っていたであろう時間やお金のことを考えてうんざりするが、
間違っても非効率的な作業をしていた自分を責めてはいけない。
何より今日、自分は少しレベルが上がったのだ。
前を見た方がいいし、ポジティブなことにエネルギーは使った方がいい。俺は体力がないから。

今日大量のファイルに影響がある改修作業をした。
影響範囲を調べて不要なファイルを一気に削除しているときにふと、昔よりも器用に仕事をこなせている自分に気がついて嬉しくなった。
それは同時にそういう自分の変化が、日常生活にもうまく活かせていると気が付いたからだと思う。
唐突にプログラミングを学んできて良かったと思った。

頭の固い自分にはおよそ不向きであろう仕事を今までこなせてきたのも、
ひとえに辛抱強くアドバイスをしてくれた同僚のおかげだし、
やることなすこと裏目にでる自分を温かく見守ってきてくれた友人や家族、パートナーのおかげでもある。
これからも奇跡に期待せず、人からは笑われてしまうような小さな一歩でも自分や大切な人たちのために積み重ねて行きたい。

別に退職エントリとかではない。