creonメモ

コーディングやデザインの作業メモなど

jsonデータをソートしたりレコード選択する

f:id:iriarj:20191226090347j:plain CMSやフレームワークを使えない状態で詳細ページ同士でリンクをつなげる必要があって、フロントで何とかしたメモ。
SEOやデータ追加更新時のリソースを考えると、フロント対処というところにツッコミがありますが

かなりのレアケースとは思いますが、せっかくなので記事にします。

こういうjsonがあったとして

[
  {
    "name": "1月",
    "page": "sample1.html",
    "category": 2
  },
  {
    "name": "2月",
    "page": "sample2.html",
    "category": 1
  },
  {
    "name": "3月",
    "page": "sample3.html",
    "category": 3
  },
  {
    "name": "4月",
    "page": "sample4.html",
    "category": 1
  },
  {
    "name": "5月",
    "page": "sample5.html",
    "category": 1
  },
  {
    "name": "6月",
    "page": "sample6.html",
    "category": 2
  },
  {
    "name": "7月",
    "page": "sample7.html",
    "category": 3
  },
  {
    "name": "8月",
    "page": "sample8.html",
    "category": 3
  },
  {
    "name": "9月",
    "page": "sample9.html",
    "category": 2
  }
]

カテゴリーが数値なのでちょっとわかりにくいかもしれません。
月別データだけど月によって違うカテゴリーになっていて、 今見ているページのカテゴリーと同じ月を優先的に表示したいとします。

// 変数jsonにjsonデータが代入されている想定

// 今のページを取得
const href = location.href;

// jsonデータから、表示中のページレコードを検索
const now = json.filter(function (obj) {
  return (href.indexOf(obj.page) != -1);
}).shift();

let list = json.filter(function(element) {
  // 表示中のページは含めない
  return (now.page != element.page);
}).sort(function(a, b){
  // 表示中のページと同じカテゴリーは最優先で表示
  a.category = (now.category === a.category) ? 0 : a.category;
  b.category = (now.category === b.category) ? 0 : b.category;
  return a.category - b.category;
});

これでlistをforEachなど使うとソート済みのデータで回せます。

素のJSでスムーズスクロール

f:id:iriarj:20191226090347j:plain
JavaScript
自分用メモ

様々なサイトで用意されている「ページ最上部へ戻る」ボタンを、jQueryやライブラリを使わずに素のJSで実装するコードです。

<!-- htmlのbutton -->

<button id="scroll">ページ最上部へ</button>

「ページ最上部へ」ボタンをクリックしたら少しずつスクロール位置を戻し、小刻みに繰り返してアニメーションのように見せています。

スクロールスピードはscrollTimerで調整するようになっています。scrollTimerの値が小さいほど速くスクロールします。

document.getElementById('scroll').addEventListener("click", function() {
    
    let height = window.pageYOffset;
    let scrollTimer = 15;
    let scrollBase = height / scrollTimer;

    const scrollPageTop =()=> {

        height = window.pageYOffset;
        scrollTo(0, height - scrollBase);

        // 最上部になっていなかったらスクロールを繰り返す
        if (height != 0) {
            setTimeout(scrollPageTop, scrollTimer);
        }
    };

    scrollPageTop();
});

ページが長いと時間かかるかもしれません。

【CakePHP3.6】 一覧データをログイン中ユーザーのものに限定したいとき

f:id:iriarj:20200122092743p:plain
CakePHP3.6
自分用メモです。

個別のユーザーがusersテーブルに登録されていて、bookmarksテーブルがuser_idを持つ形で紐づいている場合

bakeしたコード↓

    /**
     * Index method
     *
     * @return \Cake\Http\Response|void
     */
    public function index()
    {
        $this->paginate = [
            'contain' => ['Users']
        ];
        $bookmarks = $this->paginate($this->Bookmarks);

        $this->set(compact('bookmarks'));
    }

↑これを↓のようにすると、ログイン中ユーザーのidに紐づく一覧データのみを表示します。

  /**
     * Index method
     *
     * @return \Cake\Http\Response|void
     */
    public function index()
    {
        $this->paginate = [
            'contain' => ['Users'],
            'conditions' => ['user_id' => $this->Auth->user('id')],
        ];

        $bookmarks = $this->paginate($this->Bookmarks);
        $this->set(compact('bookmarks'));
    }

↑これを更に↓のようにconditionsを設定すると、特定のユーザーのときはユーザー自身の一覧のみ、それ以外は全ユーザーの一覧が表示されます。

   /**
     * Index method
     *
     * @return \Cake\Http\Response|void
     */
    public function index()
    {
         $this->paginate = [
            'contain' => ['Users']
        ];

        // 一般ユーザーのときは表示する一覧データをユーザー自身のものに限定する
        if ($this->Auth->user('role') === 0) // 適宜、一般ユーザー判定など
        {
            $this->paginate['conditions'] = [
                'user_id' => $this->Auth->user('id')
            ];
        }

        $bookmarks = $this->paginate($this->Bookmarks);
        $this->set(compact('bookmarks'));
    }

【CakePHP3.6】ログイン後の遷移先設定

f:id:iriarj:20200122092743p:plain
Cakephp3.6
自分用メモです。

ログインしたときに、元のページまたは指定のページに遷移させる方法。

UsersControllerのloginメソッドなどでも対応できますが、個人の好みとAuthコンポーネントの管理コストを考えるとこの書き方が便利だと思いました。 (ついでにログアウト後の遷移先も設定)

// src/Controller/AppController.php の中で
namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{
    public function initialize()
    {
        // 既存のコード

        $this->loadComponent('Auth', [
            'authenticate' => [
                'Form' => [
                    'fields' => [
                        'username' => 'email',
                        'password' => 'password'
                    ]
                ]
            ],
            'loginAction' => [
                'controller' => 'Users',
                'action' => 'login'
            ],

            // ログイン後リダイレクト先
           'loginRedirect' => [
                'controller' => 'Articles',
                'action' => 'index'
            ],

            // ログアウト後リダイレクト先
            'logoutRedirect' => [
                'controller' => 'Users',
                'action' => 'login'
            ],

            // コントローラーで isAuthorized を使用します
            'authorize' => ['Controller'],
            // 未認証の場合、直前のページに戻します
            'unauthorizedRedirect' => $this->referer()
        ]);

        // display アクションを許可して、PagesController が引き続き
        // 動作するようにします。また、読み取り専用のアクションを有効にします。
        $this->Auth->allow(['display', 'view', 'index']);
    }
}

UsersControllerからでも出来なくはありませんが、↑の方がコードも簡潔なので特に事情がない限りはAppControllerのAuthコンポーネントでまとめると良いと思います。

loginメソッド内で設定する必要があるときは、公式チュートリアルのリダイレクトのところを設定します。

// src/Controller/UsersController.php の中で
public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);

            // ↓これの引数を指定しても実装はできる
            return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Flash->error('ユーザー名またはパスワードが不正です。');
    }
}

参考にしたページ https://book.cakephp.org/3.0/ja/tutorials-and-examples/cms/authentication.html

GitLab Pagesで静的サイトを公開する

f:id:iriarj:20200109085032p:plain

ちょっとしたサンプルなどを公開するときにも使える、GitLab Pagesを使ったページの公開方法のメモ。

masterブランチに公開したい静的サイトのデータと、.gitlab-ci.ymlをコミットしてプッシュする。

↓.gitlab-ci.ymlの内容

image: python:2.7
pages:
  stage: deploy
  script:
  - mkdir .public
  - cp -r * .public
  - mv .public public
  artifacts:
    paths:
    - public
  only:
  - master

すぐには公開されないので、しばらく時間を置く。

GitLabプロジェクトのCI/CDで「passed」になっていることを確認。

公開先のURLはhttps://ユーザー名.gitlab.io/プロジェクト名/になる。

ユーザー名はGitLabのプロフィールURLのサブディレクトリの名前、私の場合は https://gitlab.com/riya3riya3

もしくはhttps://gitlab.com/profile/accountのChange usernameから確認できる。

マウスオーバーでふわふわ動く旗

f:id:iriarj:20191226090347j:plain

タイトルのままですが、マウスをのっけると上下にふわふわ動く日本国旗です。 こんな感じ

html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>floating</title>
  <link href="https://fonts.googleapis.com/css?family=Noto+Serif+JP&display=swap" rel="stylesheet">
  <link href="css/base.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="flag">
  <div class="reiwa">
    令和
  </div>
</div>
<script src="js/base.js"></script>
</body>
</html>
CSS

htmlのhead内で読み込んでいるcss/base.cssの記述内容です。 今回はreset系のCSSは使っていません。

html {
  font-size: 62.5%;
}
body {
  background: #efefef;
  font-size: 1.6em;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}
.flag {
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 21rem;
  height: 14rem;
  position: relative;
  cursor: pointer;
}
.jump {
  animation: jump-anime 1s ease-in-out infinite;
}
@keyframes jump-anime{
  0% {
    bottom: 0;
  }
  50% {
    bottom: 2rem;
  }
  100% {
    bottom: 0;
  }
}
.reiwa {
  background: #F20000;
  border-radius: 50%;
  color: #fff;
  font-family: 'Noto Serif JP', serif;
  font-size: 2.3rem;
  line-height: 1;
  margin: 0 auto;
  padding: 1.9rem 3.05rem;
  writing-mode: vertical-rl;
}
JavaScript

htmlの下の方で読み込んでいるjs/base.jsの記述内容です。

const flag = document.getElementsByClassName('flag')[0];

flag.addEventListener('mouseover', () => {
  flag.classList.add('jump');
})

flag.addEventListener('mouseout', () => {
  flag.classList.remove('jump');
});
ちょっとだけコードの説明

bodyの子要素の国旗をflexboxで中央に指定しています。 (他に要素もないのでbodyに設定しましたが、普通のサイトではbodyに書くケースはあまりないと思います)

flagクラスが国旗部分、reiwaクラスが「令和」の文字部分と国旗中央の赤いところです。 赤の指定は公式には「紅色」なのですが、好みの赤を指定しました。 赤い丸も国旗の位置指定と同様にflexboxで中央にしています。 writing-modeにvertical-rlを指定することで文字を縦書きにします。

htmlでは使っていませんが、jumpクラスをつけると上下にふわふわ動くアニメーションを設定します。

そのjumpクラスをjavascriptでマウスオーバーイベントで追加、マウスアウトで削除させています。

今回の動きだけならjsを使わずに実装できますが、jsを使えば更にバッジつけたりウィンドウ出したりやりやすいので、どちらかといえばこちらが好みです。

画面最上部の横並びナビゲーション

f:id:iriarj:20191225083020j:plain よくあるサイトデザインの1つ、ページ一番上の横並びメニューの書き方例です。

こういうの

f:id:iriarj:20191227215820p:plain
横並びナビゲーション

今回の横並びはflexboxを使っています。

まずhtml

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>flex-box</title>
  <link rel="stylesheet" type="text/css" href="css/reset.css">
  <link rel="stylesheet" type="text/css" href="css/base.css">
</head>
<body>
<header>
  <nav> 
    <ul class="g-nav-lists">
      <li>
        <a href="#">項目1</a>
      </li>
      <li>
        <a href="#">項目2</a>
      </li>
      <li>
        <a href="#">項目3</a>
      </li>
      <li>
        <a href="#">項目4</a>
      </li>
      <li>
        <a href="#">項目5</a>
      </li>
    </ul>
  </nav>
</header>
<main>
  メインコンテンツ
</main>
</body>
</html>

このサンプルの場合はフッターや別のナビゲーションがないので<nav><ul>の構造が変に見えるかもしれませんが、フッターなどで似たようなデザインのナビゲーションを使うときにul単位で記述を共通化できるので私はけっこう好きです。

headerではreset.cssとbase.cssを読み込んでいます。
reset.cssはこちらを使用しています。
古いCSSリセットからはもう卒業!モダンブラウザに適した新しいCSSリセット -A Modern CSS Reset | コリス

CSSはこちらです。
作業内容を見やすくする目的でborderやmin-heightなど書いていますが、実際のページを作るときは不要かもしれません。

html {
  font-size: 62.5%;
}

body {
  font-family: Quicksand, 游ゴシック体, "Yu Gothic", YuGothic, "ヒラギノ角ゴシック Pro", "Hiragino Kaku Gothic Pro", メイリオ, Meiryo, Osaka, "MS Pゴシック", "MS PGothic", sans-serif;
  font-size: 1.6em;
  line-height: 2;
}

.g-nav-lists {
  display: flex;
  justify-content: center;
  list-style-type: none;
  background: #ccc;
}

.g-nav-lists li a {
  color: #222;
  text-decoration: none;
  padding: .8rem 1.5rem;
  display: block;
}

.g-nav-lists li a:hover {
  background: #555;
  color: #fff;
}

main {
  border-right: 1px solid #ccc;
  border-left: 1px solid #ccc;
  min-height: calc(100vh - 4.8rem);
  margin: 0 auto;
  max-width: 80rem;
  padding: 2rem;
}

今回はheader要素とmain要素の幅を分けています。
headerはアイキャッチ画像を使う場合もページ横幅いっぱいまで簡単に設定でき、mainは読みやすい幅で最大値を指定しています。