学習記録

プログラミングの学習中に調べて解決したこと等の記録です。

todoアプリの頭の文字しか表示されない不具合

jsprimer.net

こちらのサイトでtodoアプリを作りながらjsについて勉強していたときのこと。

 

todoアプリ画像

todoアプリ画像

写真のように、todoを投稿すると頭の1文字しか表示されない不具合が発生しました。

デベロッパーツールで追って行ったところ、中のデータはちゃんと「今日の予定」と入っているのに、画面に出力される部分で「今」だけになってしまっていることが判明。

 

原因となっていたコードがこちら。

×誤りバージョン

export function element(strings, values){

○正解バージョン

export function element(strings, ...values){

 

何が違うのか?

 

はい、第二引数のvaluesですね!

 

入力するtodoの文字数が毎回異なるため、毎回引数に入ってくる文字数の個数が異なる→「可変長引数」

引数の前に「...」をつける書き方をして「これは可変長引数ですよ」と宣言しなければいけなかった→「残余引数(レストパラメータ)」

引数の前に「...」をつけなかったことにより、引数が1個に固定されてしまい、頭の文字のみ表示された→不具合の原因

 

ということっぽいです。

 

可変長引数(かへんちょうひきすう)とはプログラミング言語において、関数やメソッドやマクロの引数が固定ではなく任意の個数となっている引数のことである。

可変長引数 - Wikipedia

 

残余引数構文により、不定数の引数を配列として表すことができます。

残余引数 - JavaScript | MDN

 

参考にさせていただいたブログ

takamints.hatenablog.jp

 

「今日の予定」で引数に丸々入るものだとばっかり思い込んでいたので、「今」「日」みたいに分かれてるって発想がそもそもありませんでした。

 

でも、「じゃあ中のデータは「今日の予定」で丸々入っていたのはなんで?」と調べてもよくわかっていない部分もあるので、分かり次第追記か別記事を書きたいと思います。

 

半分ただの覚書みたいになってしまいましたが、とりあえず新しい知識がちょっとだけでも身についたということで、一応記録までに。

 

 

 

今回は以上となります。

 

 

BoostNoteをクラウド同期させる方法

qiita.com

↑こちらを参考にしてやりましたが、画面がちょっと私がやったときは違う部分があったため、備忘録で残しておきます。

 

※BoostNoteのクラウドに保存する方法で調べるとDropBoxGoogle Driveと同期するやり方も出てきますが、2020年にリニューアルした後はBoostNoteのデフォルトの機能としてクラウド保存がついたようです。

 

 

1、BoostNoteをダウンロードする

BoostNoteダウンロード

OSに合わせて選んでください。

私は確か、MacのZip版を選んだ気がします。

 

2、中を開く

BoostNote設定

ダウンロードが終わり、アプリを開いてみるとこのような画面になっていると思います。

画像のように人が並んでいるアイコンの画面が選択されていると思いますので、アイコンなしのものを選びます。

 

3、ストレージを作る

BoostNote設定

ストレージ名横の下三角をクリックし『New Strage」を選びます。

 

4、ストレージに名前をつける

BoostNote設定

 

BoostNote設定

ストレージに名前をつけて作成すると、上のように新規ストレージができています。

 

5、クラウドにつなげる

BoostNote設定

ストレージ名の横の三角を押し「Preference」を選択。

 

BoostNote設定ストレージをクラウドとリンクするかを上の赤丸部分で設定できます。

最初の時は赤丸の部分が「ログインしないとストレージとクラウドがリンクできません」みたいな表示になっていたので、ログインの作業が入りました。

 

BoostNote設定

クラウド上のストレージの名前を決めたらリンクさせる。

 

BoostNote設定

左上のように、アイコンの横に矢印がぐるぐるしているマークがつけば、クラウドとの同期完了。

z-indexが効かないときに確認すべき3つの点について

z-indexをつけているのに重なり順が調整されず、試したことと解決方法を記載します。

 

 

 

どのようなときにおこったか

レスポンシブ対応でハンバーガーボタンを作った際、メニューが出現した時にハンバーガーボタンが消えてしまい、メニューを戻せないということがおきました。

そのときのhtmlとCSSは以下の通りとなります。

※関係のない部分は省略して記載しています。

 

html部分
<header class="header">
<!-- ハンバーガーメニュー-->
<button class="burger">
<span></span>
<span></span>
<span></span>
</button>
<!-- //ハンバーガー -->
</header>
 
<!-- メイン -->
<main>
<!-- メニュー -->
<nav class="nav">

 

CSS部分

.header {
position: fixed;
z-index: 3
 
.burger {
position: relative;
z-index: 4;
 
.nav {
position: absolute;
z-index: 2

 
ハンバーガーボタンを作動させ、メニューが出てきたときにjsで.navにつけるCSS
.js-is-open {
      transition: 0.4s;
      transform: translateX(-100%);
      display: block;
      z-index: 3;
    }

 

 

 

 背景色と同化していないか確認する

別のサイトを作った際に「メニューの色とボタンの色を同じにしていたため、メニューが出てきたら同化してしまってボタンが消えたように見えた」ということがあったため今回も真っ先にそれを疑いましたが、今回はきちんと色を変えていたので、その可能性はありませんでした。

 

 

 positionがついているか確認する

z-indexプロパティは、ボックスの重なりの順序を指定する際に使用します。 z-indexプロパティは、positionプロパティでstatic以外の値が指定されている要素に適用されます。

引用元:http://www.htmq.com/style/z-index.shtml

 

とのことだったためpositionの付け漏れがないか確認してみましたが、全部についていました。

 

 

 親要素より子要素のz-indexが大きくなっていないか確認する

今回の原因はこれでした。

 

親要素

.header {
z-index: 3
 
子要素(これが一番上に出て欲しいためz-indexを4にしているが、親の影響で実質3
.burger {
z-index: ;

 

メニュー

.nav {
z-index: 2
ハンバーガーボタンを作動させ、メニューが出てきたときにjsで.navにつけるCSS
メニューが出てきた際のz-indexは3なので、headerとburgerとは同じz-index
.js-is-open {
      z-index: 3;
    }

 

参考:https://www.netimpact.co.jp/diary/22704/

 

 

z-indexが実質すべて同じ値になっていたためボタンが埋もれてしまうということが起こっていたため、値を調整して今回は解決しました。

スムーズスクロールをした後にスクロールが効かなくなり、ボタンが点滅する不具合を解消する方法

 

 

どのようなものを作ろうとしたか

縦長のホームページでよくある「ある程度までスクロールしたら横に出てくる、ページ先頭に戻れるボタンを実装しようとしたときのことでした。

 

どのような不具合が起きたか

このとき

1、ボタンを押してページ先頭まで戻ったあと、一定時間スクロールが出来なくなる

2、スクロールが効くようになった後、ボタンが急に出たり現れたり、点滅を繰り返す

という不具合が起きました。

そのときのコードは以下のようなものです。

 

html

<!-- 上へ戻るボタン -->
    <p class="totop js-totop">
      <a href="#top" class="totop__link">to top</a>
    </p>

 

jQuery

// 上に戻るボタンの外側
  var toTopBtn = $('.js-totop');

  // ボタンが現れる高さ
  var showBtnHeight = 500;

  // 上に戻る早さ0.05秒
  var DURATION = 500;

  $(window).scroll(function(){
    // スクロール量を取得してshowBtnHeight以上であれば
    if($(this).scrollTop() > showBtnHeight) {
      // ボタンを表示
      toTopBtn.fadeIn();
    }else{
   // ボタンを非表示
toTopBtn.fadeOut(); } // ボタンをクリックしたら上までスクロール toTopBtn.click(function(){ $('body,html').animate({ scrollTop: 0 }, DURATION); return false;

 

エラーの原因の予想

エラーの原因はそれぞれ

1、ボタンを押してページ先頭まで戻ったあと、一定時間スクロールが出来なくなる

→scrollTop: 0と記載したが、何かが邪魔をしスクロールが上まで行き切ってないと判断されているため、scrollTop: 0を満たそうとしてスクロールが出来なくなったのではないか。

 

2、スクロールが効くようになった後、ボタンが急に出たり現れたり、点滅を繰り返す

→ボタンの表示に関わる記載がtoTopBtn.fadeIn();とtoTopBtn.fadeOut();のため、記載が誤っているのではないか。

 

と予想しました。

 

 

エラーの原因と修正後のコード

予想に反して、原因は1つでした。

if(scrollTop >= posBottom) { ですが、
ページ下部にいったときにこの条件が複数回呼ばれています。
よって $('body').animate({scrollTop:0},2000); も複数回呼ばれます。
ページトップに移動した後も実際には呼ばれた回数実行されているので、その間はさらにスクロールしても scrollTop:0 に移動されているので、スクロールができないような状態になっています。

引用元:javascript - animate()で上端までスムーズスクロールしたあとにスクロールがしばらく効かなくなります。 - スタック・オーバーフロー

 

 

if文の判定はボタンが現れる消えるの境い目でのみ行われているのかと思いきや、スクロールに合わせて何度も行われていたため、ページトップに戻った後も

 

溜まっていたif文の判定の回数だけボタンが現れたり消えたりを繰り返し

$(window).scroll(function(){
    // スクロール量を取得してshowBtnHeight以上であれば
    if($(this).scrollTop() > showBtnHeight) {
      // ボタンを表示
      toTopBtn.fadeIn();
    }else{
      toTopBtn.fadeOut();
    }

 

この scrollTop: 0が何度も呼ばれていたため一定時間スクロールが効かなかった

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

 

のだということがわかりました。 

 

記事を参考にstop()をつけ、上に戻った場合はあとに溜まっているアニメーションをすべてクリアにすることにより、どちらの不具合も解消させることができました。

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

stop() - jQuery 日本語リファレンス

 

 

不具合を受けて

不具合が2つ起きたため原因は別々のところにあると思いきや、同じことが根底にあったというのにはとても驚きました。

複数の不具合が同時に発生したとしても慌てて一度に対処しようとするのではなく、冷静に1つずつ取り組んでいけば早期に解決できるため、1つ1つ確実に解消することを今後心がけていきます。

  

継承の際のindstance ofの使い方

 

 どのような状況だったのか

ゲーム作成を通し、PHPオブジェクト指向を学習中のこと。

 

 

どのような結果にしたかったかと実際の結果

どのクラスのインスタンスかによって魔法攻撃ができたり、回復魔法が使えたり挙動を変えたかったので、

Monsterクラス(親クラス)

MagicMonsterクラス(子クラス、Monsterクラスを継承)

HealMonsterクラス(子クラス、継承の継承、Monsterクラスから見れば孫?クラス)

とし、

 

 
<?php

if ($_SESSION['monster'] instanceof MagicMonster) {
 
〜MagicMonsterにさせたい行動〜

 } else if($_SESSION['monster'] instanceof HealMonster){
 
〜HealMonsterにさせたい行動〜
 
 } else if($_SESSION['monster'] instanceof Monster){
 
~Monsterにさせたい行動〜
 
 }

?>
 

と最初書いていました。

 

 その結果

 ところが、

「HealMonster が出てきているはずなのに、ifの分岐でMagicMonsterに入ってしまっていて、MagicMonsterの挙動をしている」

 

「MagicMonsterとMonsterが出てきたときはちゃんと正しい分岐に入って、意図した挙動をしている」

 

ということがおき、HealMonsterの時のみ思った通りに動いていないという結果になりました。

 

 

解決策

  if文の順番を、

MagicMonsterクラス(子クラス)

HealMonsterクラス(孫?クラス)

Monsterクラス(親クラス)

 

ではなく

 

HealMonsterクラス(孫?クラス)

MagicMonsterクラス(子クラス)

Monsterクラス(親クラス)

にしたところ、解決。

 

<?php
if ($_SESSION['monster'] instanceof HealMonster) {
 
〜HealMonsterにさせたい行動〜

 } else if($_SESSION['monster'] instanceof MagicMonster{
 
〜MagicMonsterにさせたい行動〜
 
 } else if($_SESSION['monster'] instanceof Monster){
 
~Monsterにさせたい行動〜
 
 }
?>

 

 

なぜそれで解決したのか?

試しにMonsterクラスをif文の先頭に持ってきたところ、MagicMonster が呼ばれているときも、HealMonsterが呼ばれているときも、ifの分岐がMonsterクラスに入っていました。

 

そのため仮説として

「HealMonsterクラスはMonsterクラスとMagicMonsterクラスを継承していて、その2つの要素も持っているため、instanceof で判定すると、親要素のほうでも反応する」

ということなのではないか、と考えていましたが、PHPマニュアルを見たところ

 

instanceof は、ある変数が 特定の親クラスを継承したクラスのオブジェクトのインスタンスであるかどうかを調べることもできます。

例2 継承したクラスでの instanceof の使用法

<?php
class ParentClass
{
}

class 
MyClass extends ParentClass
{
}

$a = new MyClass;

var_dump($a instanceof MyClass);
var_dump($a instanceof ParentClass);
?>

上の例の出力は以下となります。

bool(true)
bool(true)

 引用 PHPマニュアルhttps://www.php.net/manual/ja/language.operators.type.php

 

 と記載がありました。

 

「HealMonster が出てきているはずなのに、ifの分岐でMagicMonsterに入ってしまっていて、MagicMonsterの挙動をしている」

 

という動きをしていると私が思っていたときのPHP側の実際の動きとしては、

 

HealMonsterをinstanceofで調べたら、MagicMonsterを親クラスとして継承したクラスだったので、if ($_SESSION['monster'] instanceof MagicMonster のルートに入っていた」

  

ということで、挙動自体はおかしくなく、instance ofの使い方をきちんと理解していないことが今回のつまづきの原因でした。

 

 最後に 

マニュアルが意図していたことに気付いたのが、解決策を思いついて「結局なんでなだろう?」ともう一度見直したときだったので、1度で情報が得られなかったときも、繰り返し確認することが必要だと身に染みて感じました。

 

画像登録の際のバリデーションが効かない問題

 

 

 

サイズオーバーの画像が登録されてしまうという問題

<?php
// バリデーション
case UPLOAD_ERR_OK:
debug('写真のバリデーションOK');
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('ファイルが選択されていません');
case UPLOAD_ERR_INI_SIZE:
throw new RuntimeException('画像を5MB以下にしてください');
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('画像を5MB以下にしてください');
default:
throw new RuntimeException('アップロードエラー');
 }
?>

このよう5MB以上の画像を登録しようとするとエラーメッセージが発生するバリデーションをかけていましたが、明らかに大きいサイズの画像を登録しようとしてもエラーが表示されず、そのまま通ってしまうということが起きました。

 

詳しい状況として

1:大きいサイズの画像を登録しようとする

2:「登録」ボタンを押してもPOST送信の処理が走らず、登録前の画像が表示される

3:画面にエラーの表示は出ない、もしくは「ARRAY」と表示される

 

原因

ini_set 関数でログはデフォルトとは別なところに出るようにしており、そちらばかり見ていましたが、デフォルトのphp_error.logをみたところ

PHP Warning: POST Content-Length of 12071505 bytes 
exceeds the limit of 8388608 bytes in Unknown on line 0 

というエラー。

 

調べてみたところ、画像登録の際に気にしなければいけない部分はphp.ini のmemory_limit、post_max_size、upload_max_filesizeの3つ。

 

post_max_size integerPOSTデータに許可される最大サイズを設定します。この設定は、ファイルアップロードにも影響します。大きなファイルをアップロード するには、この値を upload_max_filesize より大きく設定する必要があります。 一般的に memory_limit は、 post_max_sizeよりも大きく する必要があります。 integerを使用する際、 その値はバイト単位で測られます。 この FAQ に記載された 短縮表記を使用することも可能です。 POSTデータの大きさが、post_max_sizeより大きい場合、 $_POST と $_FILES superglobals は空になります。この事象は、いくつかの方法で検出することができます。 例えば、$_GET 変数をデータを <form action="edit.php?processed=1">のように 処理するスクリプトに渡し、 $_GET['processed'] が設定されているかどうかを 確認する方法があります。

引用元:PHPマニュアル

https://www.php.net/manual/ja/ini.core.php#ini.post-max-size

 

とあり、今回、「登録ボタンを押してもPOST処理にいかずはじかれた」、「エラー表示でARRAYと出る」の2点は、POSTデータよりpost_max_sizeが小さかったから起こったんだなということがわかりました。

 

php.iniの設定を直したらバリデーションが効くようになり、解決しました。

 

 

参考:http://blog.prophet.jp/3173

 

入力保持可能なものと不可能なもの

 

 

問題点「入力保持がきかない」

やりたかったこと

textareaタグで作成したフォームに文字を入力した際に、バリデーションエラーでひっかかったとしても入力内容がそのまま保持されるようにする。

実際

バリデーションでひっかかるとtextareaの部分だけ入力内容が消えてしまう。

 

 

inputで作成したフォームではそのまま入力内容が保持されているので、PHPのコードの書き方が誤っているわけではなさそうでした。

 

 

解決策

<input type="text" name='email'
              value='<?php if (!empty($_POST['email'])) echo $_POST['email']; ?>'>

inputタグを使用した際に入力保持をしたいときには、上記のようにvalue属性をつけて入力保持のコードを書きますが

 

<textarea name="comment"> 
<?php if (!empty($_POST['comment'])) echo $_POST['comment'] ?></textarea>
 

textareaの場合は、value属性は付けられないため、開始のタグと閉じタグの間にコードを入れるようにすると効くようになりました。

 

投稿した写真の入力保持はできるのか? 

imput type="file"にもvalue属性はつかず、閉じタグもないので入力保持はできないようです。

そもそも問題、セキュリティ面から画像ファイルの入力保持は出来ないようになっているとのことです。

 

 

参考:

https://web-kiwami.com/post-589.html

https://teratail.com/questions/115016