20201114のJavaScriptに関する記事は27件です。

【javaScript / jQuery】Googleドライブに保存した画像をHTMLで表示させる

はじめに

初めての投稿です。自分の復習も兼ねて、Qiitaに記事投稿していこうと思います。
至らない点があったらスミマセン。
今回JavaScriptでご紹介していますが、変数にGoogleドライブのURLを格納できる言語でしたらどの言語でも問題ありません。
というより、imgタグに直接記述するだけならHTMLのみで完結します。

内容

javaScript,jQueryを利用してGoogleドライブの画像をHTMLに引っ張ってくるという趣旨の記事になります。

趣味でよくiPadを使ってイラストを描くのですが、描いた絵を自分の開設しているWebページへ表示させる時にPCに送ってサーバに送って・・・という作業が面倒だったので、iPadから直接Googleドライブに画像を置いて、そこからプログラムで引っ張ってくればいいじゃないかと思ったのが始まりです。

Googleドライブに画像を保存している前提で執筆していきます。

Googleドライブに保存した画像の設定を変更する

コンテンツを右クリックし、「リンクを取得」を選択します。
スクリーンショット 2020-11-13 6.20.36.png

権限を「制限付き」から、「リンクを知っている全員」に変更します。

スクリーンショット 2020-11-13 6.25.51.png

この時、リンクのURLは下記のようになっています。
https://drive.google.com/file/d/画像ごとに異なるID/view?usp=sharing

この[画像ごとに異なるID]をjavaScriptに記述し、HTMLで表示させようという流れになります。

またこちらのリンクへ実際に移動してみると分かるのですが、閲覧者側では違うURLとして表示されます。
このように、ファイルを確認するページ別にURLが異なるのですが、HTMLの<img>を利用して画像を表示させる場合は下記のURLを利用します。

http://drive.google.com/uc?export=view&id=画像ごとに異なるID

画像のIDを変数に格納する

変数に画像のIDを格納していきます。ファイルIDは「XXXX」と表記しています。
今回はjQueryのbefore要素も利用しています。

index.html
<!-- <head>等、一部省略 -->
<body>
    <div class="container">
      <h4>画像を表示</h4>
      <div id="driveImg"></div>
    </div>
 <!-- jQueryでbefore要素を利用。今回の場合、containerとdriveImgの間に画像が挿入される -->
</body>
script.js
const URL = "http://drive.google.com/uc?export=view&id=";
const item = "XXXX";
//XXXX : Googleドライブの共有リンクのURLから確認したファイルのID

const driveURL = URL + item;
//driveURL = "http://drive.google.com/uc?export=view&id=XXXX"; となった

$('#driveImg').before('<img src="' + driveURL + '">');
/*
<div id="driveImg"></div>の後ろに、
<img src="http://drive.google.com/uc?export=view&id=XXXX">が挿入される。
*/

結果

スクリーンショット 2020-11-14 22.46.27.png

画像が表示されました。
なお2020年11月現在、ファイルのIDの文字数は33文字で統一されています。

内容は以上となります。
自分はいろんな画像のIDを配列に格納したので、その名残でJavaScriptとしてのファイルもご紹介したのですが、imgタグのみでも実装できるので初学者の方でも比較的簡単に実現できる内容だと思います。

実際やってみると分かるのですが、重いファイルをこれで表示させるとなかなか時間がかかります。
趣味で描いているイラストのサイズは2500*3000px程度であることが多いのですが、このくらい大きな画像になってしまうと読み込みに失敗してしまうことがたまにあります。

軽いファイルにならこの方法は十分使えるかもしれませんね。
良ければ応用してみてください。

閲覧ありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript / jQuery】Googleドライブに保存した画像をHTMLで表示させる

はじめに

初めての投稿です。自分の復習も兼ねて、Qiitaに記事投稿していこうと思います。
至らない点があったらスミマセン。
今回JavaScriptでご紹介していますが、変数にGoogleドライブのURLを格納できる言語でしたらどの言語でも問題ありません。
というより、imgタグに直接記述するだけならHTMLのみで完結します。

内容

JavaScript,jQueryを利用してGoogleドライブの画像をHTMLに引っ張ってくるという趣旨の記事になります。

趣味でよくiPadを使ってイラストを描くのですが、描いた絵を自分の開設しているWebページへ表示させる時にPCに送ってサーバに送って・・・という作業が面倒だったので、iPadから直接Googleドライブに画像を置いて、そこからプログラムで引っ張ってくればいいじゃないかと思ったのが始まりです。

Googleドライブに画像を保存している前提で執筆していきます。

Googleドライブに保存した画像の設定を変更する

コンテンツを右クリックし、「リンクを取得」を選択します。
スクリーンショット 2020-11-13 6.20.36.png

権限を「制限付き」から、「リンクを知っている全員」に変更します。

スクリーンショット 2020-11-13 6.25.51.png

この時、リンクのURLは下記のようになっています。
https://drive.google.com/file/d/画像ごとに異なるID/view?usp=sharing

この[画像ごとに異なるID]をJavaScriptに記述し、HTMLで表示させようという流れになります。

またこちらのリンクへ実際に移動してみると分かるのですが、閲覧者側では違うURLとして表示されます。
このように、ファイルを確認するページ別にURLが異なるのですが、HTMLの<img>を利用して画像を表示させる場合は下記のURLを利用します。

http://drive.google.com/uc?export=view&id=画像ごとに異なるID

画像のIDを変数に格納する

変数に画像のIDを格納していきます。ファイルIDは「XXXX」と表記しています。
今回はjQueryのbefore要素も利用しています。

index.html
<!-- <head>等、一部省略 -->
<body>
    <div class="container">
      <h4>画像を表示</h4>
      <div id="driveImg"></div>
    </div>
 <!-- jQueryでbefore要素を利用。今回の場合、containerとdriveImgの間に画像が挿入される -->
</body>
script.js
const URL = "http://drive.google.com/uc?export=view&id=";
const item = "XXXX";
//XXXX : Googleドライブの共有リンクのURLから確認したファイルのID

const driveURL = URL + item;
//driveURL = "http://drive.google.com/uc?export=view&id=XXXX"; となった

$('#driveImg').before('<img src="' + driveURL + '">');
/*
<div id="driveImg"></div>の後ろに、
<img src="http://drive.google.com/uc?export=view&id=XXXX">が挿入される。
*/

結果

スクリーンショット 2020-11-14 22.46.27.png

画像が表示されました。
なお2020年11月現在、ファイルのIDの文字数は33文字で統一されています。

内容は以上となります。
自分はいろんな画像のIDを配列に格納したので、その名残でJavaScriptとしてのファイルもご紹介したのですが、imgタグのみでも実装できるので初学者の方でも比較的簡単に実現できる内容だと思います。

実際やってみると分かるのですが、重いファイルをこれで表示させるとなかなか時間がかかります。
趣味で描いているイラストのサイズは2500*3000px程度であることが多いのですが、このくらい大きな画像になってしまうと読み込みに失敗してしまうことがたまにあります。

軽いファイルにならこの方法は十分使えるかもしれませんね。
良ければ応用してみてください。

閲覧ありがとうございました!

追記

編集リクエストを頂いたので一部修正しました。
こんな機能もあるんですね・・・ありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryでタブをマウスオーバー した際に表示画面を切り替える

はじめに

今回はjQueryを使って、tabをマウスオーバーした際にページリロードを行わずに表示を切り替える方法を記述していきます。

完成イメージ

mypageTabChange.gif

環境

MacOS 10.15.7
ruby 2.6.5
Ruby on Rails 6.0.0
jquery 3.4.1

前提条件

  • jQueryが導入済みであること。

それでは作業していきます!

①show.html.erbとcssを作成する。

まずはhtmlから作成します。今回はshow.html.erbというファイルに記述します。

show.html.erb
<div class="container">
  <%# tab部分 %>
  <ul class='user-nav-bar'>
    <li>
      <a href="#" id="top", class='user-nav active'>
        TOP
      </a>
    </li>
    <li>
      <a href="#" id="post", class='user-nav'>
        POST
      </a>
    </li>
    <li>
      <a href="#" id="favorite", class='user-nav'>
        Bookmark
      </a>
    </li>
    <li>
      <a href="#" id="post", class='user-nav'>
        message
      </a>
    </li>
  </ul>
  <%# 表示部分 %>
  <ul class="pages">
    <li class="page show">
      <div class="change-page">
        トップページ
    </div>
    </li>
    <%# POSTページ %>
    <li class="page">
      <div class="change-page">
        ポストページ
      </div>
    </li>
    <%# Bookmarkページ %>
    <li class="page">
      <div class="change-page">
        ブックマークページ
      </div>
    </li>
    <%# Messageページ %>
    <li class="page">
      <div class="change-page">
        メッセージページ
      </div>
    </li>
  </ul>
</div>

続いてSCSS

show.scss
// TOP・POST・Bookmark・Messageのtabセレクター==========================
.user-nav-bar {
  display: flex;
  justify-content: right;
  width: 20vw;
  margin: 0 0 0 1.5vw;
  a {
    color: rgba($color: #ffffff, $alpha: 0.3);
    a:hover {
      color: #00bfff;
    }
  }
  li {
    background-color: rgba($color: #222222, $alpha: 0.4);
    padding: 15px;
    font-size: 2vh;
  }
}
ul.user-nav-bar li .active {
  color: #ffffff;
  text-decoration: none;
}
.user-nav:hover {
  color: #00bfff;
  text-decoration: none;
}

// TOP・POST・Bookmark・Messageの表示画面==================================
.pages {
  height: 100vh;
}
.page {
  background-color: rgba($color: #222222, $alpha: 0.4);
  height: auto;
  margin: 0 auto;
  display: flex;
  justify-content: space-around;
  align-items: center;
  display: none;
  padding: 1.5vh 1.5vw;
}

.change-page {
  display: flex;
  justify-content: space-around;
  height: 500px;

以上となります。

ここまでの完成イメージは以下の通りになるかと思います。
changepreviews.png

②JavaScriptに画面を切り替える記述を行う。

tabにマウスを乗せた時に、画面を切り替える処理をjsファイルに記述していきます。
今回は、show.jsというファイルに記述していきます。

show.js
$(function() {
  // class="user-nav"と設定しているDOM要素を取得してtabsという変数名で定義する。
  let tabs = $(".user-nav");

  // クラス切り替えをtabSwitch関数という名前で定義する。
  function tabSwitch() {
    // 全てのactiveクラスの中から、"active"という要素を削除
    $(".active").removeClass("active");
    // クリックしたタブに"activeクラス"を追加する。
    $(this).addClass("active");
    // 何番目の要素(タブ)がマウスオーバー されたのかを配列tabsから要素番号を取得して、変数indexに代入
    const index = tabs.index(this);
    // 全てのpageクラスから"show"という要素を削除して、マウスオーバーしたタブに対応したpageクラスに"showクラス"を追加する。
    $(".page").removeClass("show").eq(index).addClass("show");
  }
  // タブがマウスオーバーされるとtabSwitch関数が呼び出される。
  tabs.hover(tabSwitch);
});

以上となります。

完成形はこちらになります。
mypageTabChange2.gif

終わり

ご覧いただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「画像でゴミ分類!」アプリ作成日誌day7~サイドバーのスライドメニュー化~

はじめに

「画像でゴミ分類!」アプリ作成日誌7日目の今日はサイドバーのスマホ対応ということでHTML,CSS,JavaScriptをガッツリ書いて、横から出てくるスライドメニューを実装していく回です。

<記事一覧>

前回までのあらすじ

前回までの記事では画像認識アプリを実装してDjangoに載せたうえで、Bootstrapを使ってフロントエンドを整えるところまでやりました。サイドバーに関してはスマホの際は非表示にするような設定にしていましたが、それでは味気ないのでボタンをタップしたら表示されるようにしたいと思います。

garbage/templates/garbage/base.html
# トグル用のボタンの設置
        <button class="toggler d-md-none display-toggler-inline" type="button" id="toggler" onclick="Sidebar()">
            <i class="fas fa-bars fa-2x"></i>
        </button>

Font Awesomeを利用してアイコンを設置していきます。d-md-noneというクラスを設定することで、画面が小さい時しか表示しないというのをBootstrapで制御しています。なお、Sidebar()は何もしない関数にしてあります。

次にボタンクリック時に表示されるサイドバーを作っていきます。

garbage/templates/garbage/base.html
<body>
    <div id="side-bar" class="display-sidebar-none d-md-none">
        <div type=button id="close-button" class="mr-1" onclick="Sidebar()">
            ×
        </div>
        <p role="button" class="mb-2 btn border-dark rounded-0 btn-secondary">外部リンク集</p>
        <a href="https://manage.delight-system.com/threeR/web/bunbetsu?menu=bunbetsu&jichitaiId=kashiwashi&areaId=22125&areaName=%2F&lang=ja&benriCateId=7&bunbetsuCateId=7&faqCateId=%2F&howToCateId=&search=%E3%83%9A%E3%83%83%E3%83%88%E3%83%9C%E3%83%88%E3%83%AB&dummy=" class="btn btn-default border-dark mb-1 rounded-0" role="button" target="_blank" rel="noopener noreferrer">分別検索</a>
        <a href="https://manage.delight-system.com/threeR/web/benri?menu=benri&jichitaiId=kashiwashi&areaId=22125&benriCateId=7&bunbetsuCateId=7&faqCateId=%2f&lang=ja" class="btn btn-default border-dark mb-1 rounded-0" role="button" target="_blank" rel="noopener noreferrer">ごみの分け方・出し方</a>
    </div>

これは、ほかの要素にかぶせて使うので、body直下に置いています。3~5行目で閉じるボタンを文字で直接実装しています。それ以降は中身なので、PC版の記述をそのまま利用しています。

そして、CSSは以下のようにすると、ほかの要素の上にかぶせて表示することができます。

garbage/static/garbage/css/style.css
#side-bar{
    position: fixed;
    z-index: 1;
    height: 100vh;
    width: 250px;
    opacity: 0.9;
    background-color: white;
}

#close-button{
    float: right;
    font-size: 400%;
    font-weight: bold;
    height: 90px;
}

positionで固定位置に表示できるようにし、height,widthで大きさを指定します。また、z-indexで前面に表示するようにしています。
閉じるボタンについてもいい感じになるようにしています。

JavaScriptの実装

ここまでで基本的な形はできたので、JSで動きを実装していきます。
まず、JavaScriptで追加・削除するためのクラスを書いていきます。

garbage/static/garbage/css/style.css
.display-sidebar-none{
    transition: all 0.5s;
    left: -250px;
}

.display-sidebar-inline{
    transition: all 0.5s;
    left: 0;
}

.display-toggler-none{
    transition: all 0.3s;
    opacity: 0;
}

.display-toggler-inline{
    transition: all 1.5s;
    opacity: 1.0;
}

やっていることとしては、上二つでsidebarの位置を動かします。こうすることであたかも左から出てきたかのような効果を演出できます。
また、下二つでtoggleボタンの表示非表示を切り替えています。fadeのようなことをしたかったので、透明度を変化させます。
それぞれ、transitionを設定することで、クラスを付与した際に起こる変化をゆっくりにすることができ、視覚的になじみやすく表現することができます。

それではいよいよJavaScriptの実装です。

garbage/templates/garbage/base.html
    <script>
        function Sidebar(){
            document.getElementById("side-bar").classList.toggle("display-sidebar-none");
            document.getElementById("side-bar").classList.toggle("display-sidebar-inline");
            document.getElementById("toggler").classList.toggle("display-toggler-none");
            document.getElementById("toggler").classList.toggle("display-toggler-inline");
        }
    </script>

classList.toggleでクラスがあれば削除し、なければ追加ということを一括で処理しています。

実際に実装できたものは以下のような挙動になります。めっちゃちゃんと動いてる!!!
サイドバー.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

0歳娘「パパ、型を作る関数はないの?」

2015年、とある休日

よめ太郎「あ・・・あんた!」
よめ太郎「こっち来て!!!」

ワイ「ど、どうしたん?」
ワイ「えらい大っきい声出して」

よめ太郎「娘ちゃんが初めて立ったんや!」

娘(0歳)「ヨチ・・・ヨチ・・・」

ワイ「おお・・・!」
ワイ「これはめでたいこっちゃ・・・!」

娘「・・・パパァ・・・」

ワイ「!?」
ワイ「いま、パパ言うたで!?」
ワイ「立ったと同時に言葉も喋れるんか!?」
ワイ「なんて末恐ろしい子や・・・!」

娘「・・・エテ・・・教エテ・・・」

ワイ「な、なんや喋っとるで・・・」
ワイ「なんや、娘ちゃん・・・?」

娘「・・・パパ・・・」
娘「・・・プログラミング・・・教エテ・・・」

ワイ「ファッ!?」
ワイ「Hello, World!でええか・・・?」

よめ太郎「いや、Hello, World!産声で言うてたわ

ワイ「せやった!」
ワイ「お医者さんが思わず・・・」

医者「リアルHello Worldしてどないすんねん」

ワイ「ってツッコんでたもんな」
ワイ「ほな、今日は何を教えてほしいんや?娘ちゃん・・・」

娘「・・・ええと・・・」
娘「実は、こないだ6ヶ月検診で行った病院から依頼を受けて」
娘「とある管理画面のフロントエンド部分を開発してるの」

ワイ「もう実務!?
ワイ「凄いやないか・・・」

よめ太郎「普通に受け入れてるお前が凄いわ」

ワイ「それで、どんな管理画面なんかいな?」

娘「幼児たちの身体測定の結果を記録するための管理画面なの」

ワイ「ふむふむ」

娘「それでね?」
娘「子供たちの身長とか体重、氏名などを記録する必要があるから」
娘「TypeScriptで、こんなを定義したの」

type Child = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

ワイ「なるほどな」

  • 子供は、姓・名・年齢・身長・体重というプロパティを持っている。

ワイ「・・・ということを表しているんやな」
ワイ「ええ感じやないか」
ワイ「それで、いったい何が分からへんのや?」

娘「えっとね」
娘「氏名や年齢を入力して、子供たちの情報を検索する画面があるんだけど」
娘「そこで使う型が上手く書けないの」

ワイ「と言いますと?」

娘「例えば、氏名で検索する場合は」

const condition: Child = {
    lastName: "やまだ",
    firstName: "たかし"
}

娘「↑こんな感じの条件になるんだけど」
娘「型が合わなくて、エラーになっちゃって」

次のプロパティがありません:
age, height, weight

娘「↑こんなエラーが表示されちゃうの」

ワイ「なるほどな」
ワイ「Child型のはずなのに、姓と名しか持ってないから・・・」

年齢身長体重が足りまへんがな!」

ワイ「って怒られとるわけやな」
ワイ「そういう場合は、こんな型を書けばええで」

type PartialChild = {
    lastName?: string
    firstName?: string
    age?: number
    height?: number
    weight?: number
}

ワイ「プロパティ名の後ろに?を付けてやるんや」
ワイ「姓・名・年齢・身長・体重というプロパティを、全て持っててもいいし」
ワイ「一部だけ持っててもいい、そんな型や」
ワイ「ちなみにPartialってのは、部分的って意味や」

const condition: PartialChild = {
    lastName: "やまだ",
    firstName: "たかし"
}

ワイ「↑ほら、PartialChild型にしたらエラーが消えたで」

娘「パパ、そんなことは知ってるの

ワイ「ファッ!?」

娘「そうじゃなくて」

type Child = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

type PartialChild = {
    lastName?: string
    firstName?: string
    age?: number
    height?: number
    weight?: number
}

娘「↑こんな風に」
娘「ほぼ同じ型を2回も書くのが冗長だから」
娘「もっと効率的な書き方はないのか、ってことを聞きたいの」

ワイ「ぐぬぬ・・・」

娘「Child型を元にPartialChild型を作ってくれるような」
娘「そんな、型を作る関数みたいなのはないの?」

ワイ「誠に残念ながら、ございませんねや・・・」

よめ太郎「あるで」

ワイ「ファッ!?」

Partial<Type>

よめ太郎「ほな、コードを書きながら説明していくで」
よめ太郎「Child型を元にPartialChild型を作るには・・・」

type PartialChild = Partial<Child>

よめ太郎「↑こうや」

娘「へえ、PartialってやつにChild型を渡すと」
娘「さっきのPartialChild型と同じものを作って返してくれるんだ!」

よめ太郎「せや」

娘「じゃあ、逆にPartialChild型からChild型を作るようなやり方もあるの?」

よめ太郎「あるで」

Required<Type>

type PartialChild = {
    lastName?: string
    firstName?: string
    age?: number
    height?: number
    weight?: number
}

よめ太郎「↑この型を元に・・・」

type RequiredChild = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

よめ太郎「↑この型を生成したい場合は・・・」

type RequiredChild = Required<PartialChild>

よめ太郎「↑こうや!」

娘「へえ、Requiredっていうやつに、PartialChildを渡すと」
娘「全部のプロパティから?マークを取ってくれるんだね!」

よめ太郎「そんなイメージや」

Readonly<Type>

娘「じゃあママ」
娘「次は・・・プロパティを上書きできないreadonlyな型を作って!」

よめ太郎「ええで」
よめ太郎「つまり・・・」

type Child = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

よめ太郎「↑このChild型を元に・・・」

type ReadonlyChild = {
    readonly lastName: string;
    readonly firstName: string;
    readonly age: number;
    readonly height: number;
    readonly weight: number;
}

よめ太郎「↑こんな型を作ればええんやろ?」

娘「そう!」

よめ太郎「その場合は・・・」

type ReadonlyChild = Readonly<Child>

よめ太郎「↑こうや!」

娘「わぁ、便利だね!」

Omit<Type, Keys>

娘「じゃあママ」
娘「Child型から、heightプロパティとweightプロパティだけ削って」

type NameAndAge = {
    lastName: string
    firstName: string
    age: number
}

娘「↑こんな型を作ることもできる?」

よめ太郎「できるで」

type NameAndAge = Omit<Child, "height" | "weight">

よめ太郎「↑こうや!」

娘「わあ、便利!」

ワイ「(いつ便利やねん・・・)」

※筆者注:割と使うときあります。

ReturnType<Type>

娘「じゃあママ」
娘「例えば・・・」

const sameCheck = (a: number, b: number): boolean => a === b

娘「↑こんなsameCheckっていう関数があるとして」
娘「この関数の、戻り値の型を求めることはできる?」

よめ太郎「できるで」

type SameCheckReturnType = ReturnType<typeof sameCheck>

よめ太郎「↑こうや」

娘「へえ、ReturnTypeってやつにtypeof sameCheckを渡すと」
娘「sameCheck関数の戻り値の型を取得できるんだ!」

よめ太郎「せや」
よめ太郎「今回の場合、SameCheckReturnType型はboolean型になるっちゅうことやな」

娘「なるほどね〜」

よめ太郎「もしsameCheck関数の戻り値の型が変わった場合でも」
よめ太郎「SameCheckReturnType型も追従して変わってくれるから」
よめ太郎「ベタ書きでbooleanって書くより便利やで」

娘「なるほどね!」
娘「ある関数の型を変更した場合に」
娘「連動して他の部分の型も変わってくれるってことだね」
娘「たまに便利そうだね!」

よめ太郎「そういうことや」

Parameters<Type>

よめ太郎「逆に、ある関数の引数の型を取得したい場合は」

type SameCheckParametersType = Parameters<typeof sameCheck>

よめ太郎「↑こうや」

娘「へえ、sameCheck関数には引数が2つあるけど」
娘「どんな形式で返ってくるの?」

type SameCheckParametersType = [a: number, b: number]

よめ太郎「↑こんな感じや」

娘「へぇ、引数の型はタプル型で返ってくるんだね」

よめ太郎「せや」

Exclude<Type, ExcludedUnion>

娘「じゃあママ」

type Food = "カレー" | "オムライス" | "ラーメン" | "餃子"

娘「↑このFood型から、中華料理だけを除外したい場合は?」

type FoodExcludeChinese = Exclude<Food, "ラーメン" | "餃子">

/*
FoodExcludeChinese型は
"カレー" | "オムライス"
となる
*/

よめ太郎「↑こうやな」

type Chinese = "ラーメン" | "餃子"
type FoodExcludeChinese = Exclude<Food, Chinese>

よめ太郎「↑こうでもええで!」

娘「なるほど〜」
娘「Excludeって、除外するって意味だもんね!」

Extract<Type, Union>

娘「じゃあママ」

type Hoge = 1 | 2 | 3 | 'a' | 'b' | 'c' | true | false

娘「↑このHoge型から、truefalseだけ除外したい場合は?」

よめ太郎「ええと、要はbooleanを除外すればええから」
よめ太郎「さっきのExcludeを使って・・・」

type HogeStringNumber = Exclude<Hoge, boolean>

/*
HogeStringNumber型は
1 | 2 | 3 | "a" | "b" | "c"
となる
*/

よめ太郎「↑こうやな」
よめ太郎「もしくは」
よめ太郎「Hoge型から数値と文字列だけ抽出した型を作る、と考えて」

type HogeStringNumber = Extract<Hoge, string | number>

よめ太郎「↑こうでもええで」
よめ太郎「Extract抽出するって意味やからな」

娘「へえ〜」

型関数

娘「すごいね」
娘「引数として型を渡すと、別の型が返ってくる・・・」
娘「型を作る関数みたいだね!」

よめ太郎「まさにそんな感じやな」
よめ太郎「せやから、型関数って呼んどる人たちもおるで」
よめ太郎「正式な用語やないらしいけどな」

娘「へぇ〜、型関数か〜」

よめ太郎「TypeScriptの公式サイトでは、Utility Typesって呼ばれてるみたいや」
よめ太郎「お役立ち型って意味やな」
よめ太郎「その名の通り、けっこう実務で役立つから」
よめ太郎「こんなのがある、ってことだけでも覚えておくとええで!」

娘「わかった!」
娘「ありがとう、ママ!」

よめ太郎「どういたしましてやで!」

娘「あっ!パパ!」

ワイ「なんや?娘ちゃん」

娘「ウンチ出た!」

ワイ「いやそこは赤ちゃんなんかい!」

〜おしまい〜

参考文献

この記事で書いた以外にも色々便利なのがあるから、是非読んでみてくださいやで!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript基礎知識#2

はじめに

この記事は私の脱コピペを目指すために書いている自分用のメモです。

※内容について、誤った知識やもっとわかりやすい表現等ございましたら、ご指摘いただけますと幸いです。

目次

・実行するための基本構文
・文字を出力してみる
・計算してみる
・変数とは
・var let constの違い

何かを実行させたいと気の基本構文

console.log('Hello World');
//consoleは 'Hello World'を logしなさい。
//console にあたる部分をオブジェクト
//'Hello World'にあたる部分をパラメータ
//.log()にあたる部分をメゾットという

文字を出力してみる

まずテキストエディタにファイルを準備して、
こんな感じに記述↓

main.js
console.log('Hello World');
console
Hello World

この時、
「''」(シングルクォーテーション or ダブルクォーテーション)と
「;」(セミコロン)が大切!!

計算してみる

main.js
console.log(3 + 3); //足し算
console.log(3 - 3); //引き算
console.log(3 * 3); //掛け算
console.log(3 / 3); //割り算
console.log(3 % 3); //余り出る計算

console
6
0
9
1
0

number型は''(シングルクォーテーション)がいらない!!
型についての説明は長くなってしまうのでいつか記事にしたいと思います。
今は「Javascript データ型」などで調べてみてください。

変数(Variable)とは

変数とは値を格納(代入)する箱のようなものです。

もしくは、あだ名みたいなものとの認識ではじめはよいと思います。

変数を使いこなせると、すごく長い文章を省略して使えます。
例)

function changeColor(newColor) {
  var elem = document.getElementById('para');
  elem.style.color = newColor;
}

この場合 document.getElementById('para'); を elem という変数(箱)に代入してます。
そしてその下の行で elem が document.getElementById('para'); の代わりに使われてます。 

変数名(箱の名前)には好きな名前をつけることができますが、いくつか条件があります。

・基本的に名詞(product, user_name など)
・予約値を使わない(if, else, switch など)
・大文字と小文字を区別する
・初めの文字は数字以外にする

var let const の違い

結論から書くと保守性を考慮したうえで
・原則varは使うべきではない
・変数宣言は基本的に const で
・変数への再代入が必要な場合に限り let を使う

var //再宣言・再代入可
let //再宣言不可・再代入可
const //再宣言・再代入不可

詳しくはこちらを参考にしました。([text]https://qiita.com/masarufuruya/items/096e51c3e4c36c86ae27)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React やってみたら超簡単だった話(2020年11月版)

React / Vue.js / Angular / jQuery

Angularjsが出た時に、jQueryでもういいじゃん新しい言語覚えるのめんどくせーよと思いながらやってみたら、超便利で新しい言語ちゃんと覚えないとダメだなと改心したのが遠い分昔。

その後、Angularの頻繁なバージョンアップに嫌気がさしVue.jsを使ってみたら、Angularより随分整理されてて簡単でびっくりしたのが少し昔。
Vuetify とセットで利用するとほとんどコードを書かずにやりたいことができるし、typescript を使うと記述もシンプルで安全だし、もうこれで新しい言語覚えなくてもいいかと油断したのが少し前。

とは言え、Reactも触っておいた方がいいかなと触ったら、他のフレームワークとは概念が違い戸惑ったけれども超簡単でびっくりしたのが今日。

触ってみたいけど難しそうだなと思ってる方は是非触ってみてください。

特に便利と思ったところ

jsx/tsx

js/ts に html を直接埋められる。

以下のコードは等価。

createElement.js
const header = React.createElement("header", null,
  React.createElement("h1", null, "Hello")
);
createElement.jsx
const header = <header>
    <h1>Hello</h1>
</header>

React.createElementの様な書き方しかできないならVue.js圧勝かなと思ったけれども、そんなことはなかった。Flutterもこの辺融通効くようにならないかな…。

ベタのhtmlに近い形で記述できるVue.jsの方がデザイナとの協業に強いと思っていたのだけど、Storybookを併用して開発すれば問題ないようにも思えた。

Function Component

状態を持たないコンポーネントは class ではなく、function で定義できる。
Flutter の、StatefulWidget / StatelessWidget をすごくシンプルにした感じ。

以下のコードは等価。

class-component.jsx
class Square extends React.Component {
  render() {
    return (
      <button
        className="square"
        onClick={() => this.props.onClick()}
      >
        {this.props.value}
      </button>
    );
  }
}
function-component.jsx
function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}

記述がシンプルなだけでなく、このコンポーネントは状態を持たないことが読み手に一瞬で伝わるのが素晴らしいと思った。

チュートリアル

React公式のチュートリアルが、ステップバイステップでサンプルコードを修正していくものとなっており、Reactの素晴らしさを簡単に理解できる作りとなっていた。

チュートリアル:React の導入

準備

チュートリアルのソースコードは code pen にあるため、ローカルの開発環境作成は必須ではない。
ローカルの開発環境を作成する場合、node と create-react-app のインストールを行う。

choco install nodist
nodist + 10.23.0
nodist 10.23.0
npm i -g create-react-app

2020/11/14時点では、node 8, 10, 12 で動作すると記載があったが、8.17.0, 12.19.0 では動作しなかった。

プロジェクト作成と実行

# js版作成
npx create-react-app my-app
# ts版作成
npx create-react-app my-app --typescript
# 実行
yarn start

以後は、src以下のファイルを全部消して、チュートリアルの指示通りファイルを作成し、一つずつ修正をしていけば完了します。

コンポーネント単位でファイルを分けるなども簡単にできました。

おわりに

とにかく超簡単でした。
vue-cli もそうであるが、webpack.config を手で書かなくて良くなったので誰でも気軽に始められるのでぜひお試しください。

以下の記事もあわせて読んでおくとよいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firestoreでidのわからないdocumentを取得したいときはどうすればいいのか

firestoreはフロントエンドエンジニアが個人でwebサービスを作る際に、わざわざデータベースを一から作っていく必要がなく、数行のコードでデータを保存したり、取得したりととても便利なサービスです。

ただ、使いはじめの頃、必ずと言っていいほどidのわからない(または自動で付与されたidの)documentをどうやって取得するのかという壁が立ちはだかります。

公式ドキュメントではfirebase.firestore.collection("cities").doc("SF").get()で取得できると書いてあるけど、この例でのSFと言うidがそれ以外、もしくは複数のドキュメントをgetしたい場合にはどうすればいいのかと言う話です。

結論:docを使わなくてもdocumentの取得は可能

公式リファレンスをみていただければわかると思いますが、データの取得に使うget()メソッドは返り値としてQuerySnapshotを返します。

このQuerySnapshotdocumentのidを持っていますし、このsnapshotからdocumentを取り出すことが可能です。

documentの取り出し方の一例を紹介

ここでは僕のパターン(React)を紹介します。

Example.jsx
const getPosts = () => {
    //空の配列postsを準備します
    let posts = []
    //firebaseお決まりのメソッドでコレクションからデータを取ってきます
    //ここでのポイントは「doc()がいらないこと」です
    db.collection('posts').get()
    //getしたデータに対し、
    .then(snapshot => {
        //docsプロパティ(※)を指定しforEachで各データを取り出します。
        snapshot.docs.forEach(doc => {
            const data = doc.data()
            //準備しておいた配列に取り出したデータをpushします
            posts.push({
                authorName: data.authorName,
                content: data.content,
                createdAt: data.createdAt,
                title: data.title,
                id: doc.id
            })
        })
        //ここはhooksなので気にしなくてOK
        setCurrentPost(posts)
    })
}

※マークをつけたdocsプロパティは、get()メソッドの返り値QuerySnapshotが持つ、プロパティで指定したcollectionのデータを配列として持っています。複雑ですね...w
この辺の返り値や持っているプロパティなどに関しては英語しかありませんがfirebaseの公式リファレンスに細かく説明が載っています。

番外編:documentのidを自動的に付与する方法について

ここも少し迷ったところなので、一応紹介します。

今度は逆にデータを保存する方法についてなのですが、firestoreでは自動idが推奨されていますが、この自動のdocument idをどのようにつけるのかというと、add()を使います。

Form.jsx
    db.collection('posts').add({
      title: title,
      content: content,
      authorName: authorName
    })

このようにaddを使えばデータを保存する時に自動idを生成してくれます。

ちなみにデータを追加する時にset()と言うメソッドも使いますが、これはdocumentのidを指定する必要がありますし、idを自動で生成してくれません。

「じゃあ使わなくていいじゃん」と思うかもしれませんが、これはこれで、データの編集の際に{ merge: true }と引数に指定することで、値が変わっているところだけ更新するので、無駄な書き込みが無いなどのいいところもあります。

このようにしてデータの追加の際はaddにすれば自動でidが生成されるのです。

firebaseは公式ドキュメントを読めば結構理解できる

firebaseはかなり公式のドキュメントが充実しています。

今回のようなわからないメソッドなどがあった場合はリファレンスを見れば、大切なところが全て書いてありますし、導入部分の簡単な使い方については日本語のドキュメントがあります。

ですので、firebase関連でわからないことがあったら、とりあえず公式ドキュメントをみましょうと言うお話でした?

参考

Firebase公式ドキュメント

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

セミコロンのつけ忘れを、JSはどこまで許してくれるのかを試してみた

はじめに

IT初心者の私ですが、普段はJavaScript, Node.js(以下JS)を使った開発をしています。
ここ数日、Javaを学び直そうとしているのですが、とにかくセミコロンをつけ忘れてはコンパイラに怒られています。

「JSなら実行してくれるのに・・」

むしろ逆にJavaScriptがセミコロンのつけ忘れに寛容すぎるのでは?

そのような背景もあり、JavaScriptがどこまでセミコロンのつけ忘れを許してくれるのかを、簡単に検証してみました。

この記事でわかること

  • JSだとOKだけどJava先生は怒る状態はどういうものか
  • JSが怒らないセミコロンのつけ忘れのギリギリのライン

実行環境

  • Java : paiza.io (openjdk version "15" 2020-09-15)
  • JS : paiza.io(Node.js v12.18.3)

Javaで怒られたソースコード

まずこちらがJava学習中に怒られた案件です。

import java.util.*;
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) throws Exception {
        // Your code here!
        List<Integer> names = new ArrayList<Integer>();

        names.add(1);
        names.add(2);
        System.out.println(names.get(0));
        System.out.println(names.get(1))  // ここのセミコロンつけ忘れで怒ってる
    }
}
// Main.java:12: error: ';' expected
//        System.out.println(names.get(1))
//1 error

「後ろに何も処理書いてないんだからいいじゃん!セミコロンなしでも分かってくれよ~!わかるだろ~!?」と、
セミコロンを忘れがちな私は思うわけです。
実際JSでは、後ろに処理がなければ実行できる(できてしまう)のだし。

実際JSだとどこまで許されるのか

const a = "hoge";
console.log(a);
// hoge
const a = "hoge";
console.log(a)
// hoge
const a = "hoge"
console.log(a)
// hoge
const a = "hoge"console.log(a)
// SyntaxError: Unexpected identifier
const a = ("hoge");console.log(a)
//hoge

セミコロンがなくとも、最悪改行さえしていれば実行できるようですね。
そしてこれはJavaも同様ですが、セミコロンで区切りさえすれば問題なく実行できます。

おわりに

これらの仕様に対して、良し悪しの解釈は人により分かれると思いますが、
「実行できる」とはいえ、私はセミコロンを書き込む癖をつけようと感じました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScriptの超基本】クラスの定義やメソッド・継承について簡単に解説

概要

この記事では、JavaScriptのクラスについて、超基本的な知識をメモ的にまとめています。
自分用の備忘録なのであしからず。

今回は、FCバルセロナの選手を紹介するプログラムを、クラスを用いて書いてみます。

目次

クラスとは

クラスとは特別な関数のことで、オブジェクトを効率良く作成するための設計図です。

JavaScriptは、オブジェクト指向の言語です。
オブジェクト指向とは、プログラムを現実のモノのように構成する指向のことです。

このようにオブジェクトをモノとして考えたとき、そのモノの設計図となるのがクラスです。

Webサービスなどでは、大量のオブジェクトを用いてデータを扱っています。
例えば、ログインが必要なサービスでは、ユーザーに関するデータをオブジェクトとして用いていることが多いです。

このように大量のデータを毎回ゼロから作成するのは大変なので、クラスを用いることで効率よくオブジェクトを作成しています。

クラスの定義

クラスを宣言する構文は以下に示す通りです。

index.js
class クラス名 {
    //コンストラクタやメソッドなどをここで定義
}

class クラス名と書いた後、{ }の中にコンストラクタやメソッドを定義していきます。
コンストラクタやメソッドについては後ほどまとめていきます。

では、空のクラスを用意してみましょう。

index.js
class Barcelona {

}

これで「Barcelona」クラスが用意できました。

インスタンスの生成

設計図となるクラスを用意できたら、その設計図からオブジェクトを生成します。

クラスからオブジェクトを生成するにはnew クラス名()とします。

クラスから生成されたオブジェクトのことを「 インスタンス」と呼びます。

先ほど用意したBarcelonaクラスからインスタンスを生成してみます。

index.js
class Barcelona {

}

//インスタンスを生成し、定数に代入
const barca = new Barcelona();

Barcelonaクラスから生成されたインスタンスをBarcelonaインスタンスと呼びます。
生成したインスタンスは、定数などに代入して用いられます。

この状態で定数barcaを出力すると空のオブジェクトが出力されます。

コンストラクタ

コンストラクタ」とは、classによって生成されるオブジェクトの生成や初期化を行う機能です。
コンストラクタはクラスで1つしか定義できません。

クラスの{ }の中にconstructor() { }と書くことで定義することができます。

index.js
class Barcelona {
    //コンストラクタの定義
    constructor() {
        処理
    }
}

const barca = new Barcelona();

コンストラクタの{ }の中には、処理を記述します。
ここに書いた処理は、インスタンスが生成された直後に実行されます。

また、コンストラクタの処理はインスタンスが生成されるたびに毎回実行されます。

コンストラクタに処理を書いてみます。

index.js
class Barcelona {
    constructor() {
        //コンストラクタの処理
        console.log('Hola!');
    }
}

const barca = new Barcelona();
//Hola!

コンストラクタの中に「Hola!」と出力する処理を書いてみました。

インスタンスを生成した後に、ファイルを実行するとコンソールにはコンストラクタの中に書いた処理が実行されました。

プロパティと値を追加する

クラスの目的はオブジェクトを生成することなので、プロパティと値を追加する方法を学びます。

コンストラクタの中でthis.プロパティ名 = 値とすることで、生成されたインスタンスにプロパティと値を追加することができます。

また、インスタンスはオブジェクトなので、コンストラクタの中で追加した値は、インスタンス.プロパティとすることでクラスの外で使用することができます。

練習でnameプロパティにMessiと追加してみます。さらにそれを出力します。

index.js
class Barcelona {
    constructor() {
        this.name = 'Messi';
    }
}

const barca = new Barcelona();

//生成したインスタンスを出力
console.log(barca);
//{name: Messi}

//追加した値の出力
console.log(barca.name);
//Messi

コードを見てみていきます。

constructorの中でthis.name = 'Messi';とすることで、{name: 'Messi'}というオブジェクトが生成されています。

次にconst barca = new Barcelona();barcaインスタンスを生成しています。constructorはインスタンスが生成された時に走るので、この時点でconstructorの中で定義した{name: 'Messi'}が生成されます。

そのため、生成したbarcaインスタンスを出力すると、{name: 'Messi'}と表示されます。

また、barca.nameとすることで、値を取り出すこともできています。

コンストラクタに引数を渡す

コンストラクタの()内では引数を受け取るができます。関数と同じように、()の中に引数名を記述することで、その引数をコンストラクタの処理内で使用できます。

引数を渡すには、インスタンスを生成する際のクラス名のあとの()に値を追加します。

先ほどの例を引数を使ってアップデートします。

index.js
class Barcelona {
    //引数を受け取る
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
}

//引数を渡す
const barca = new Barcelona('Messi', 10);

//それぞれの値を取り出す
console.log(barca.name);
//Messi

console.log(barca.uniformNumber);
//10

コードを見てみます。

先ほどと変わった部分は、constructorの定義部分です。引数にnameuniformNumberを渡すように書き換えています。

そのため、インスタンスを生成する際には、const barca = new Barcelona('Messi', 10);というように2つの引数を渡して生成しなければいけません。

出力は、先ほどの例と同様の結果が得られます。

メソッド

メソッド」とは、クラスの動作を定義するものになります。

class構文の中では、クラスに対して自由にメソッドを定義できます。このクラスに定義したメソッドは作成したインスタンスが持つ動作となります。

実はconstructorは、初期化時に呼ばれる特殊なメソッドです。constructorでは、情報としてプロパティを設定しました、メソッドでは動作を設定します。

メソッドはクラスの{ }の中でメソッド名() { }とすることで定義できます。メソッドは関数のようなもので、{ }の中に処理を書きます。
処理の部分では、returnを用いて値を返すこともできます。
メソッドを呼び出す際は、インスタンス.メソッド名()とします。

先ほどの例に、introduceメソッドを追加し、呼び出します。

index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    //メソッドの定義
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
    }
}

const barca = new Barcelona('Messi', 10);
//メソッドの呼び出し
barca.introduce();
//FC Barcelonaの背番号10はMessiです

では、変わった部分をみていきましょう。

class内でintroduceメソッドを定義しています。
処理の中身は、引数で受け取った値が入ったオブジェクトを使って、「FC Barcelonaの背番号〇〇は△△です」とコンソールに表示させる処理を書いています。

このようにメソッド内でインスタンスの値を使うときは、this.プロパティ名とすることで定数や変数のように扱うことができます。

インスタンスの生成時には、2つの引数を渡します。

barca.introduce();でクラスで定義したメソッドを呼び出しています。メソッドの中で書いた処理が実行され、「FC Barcelonaの背番号10はMessiです」と出力されます。

メソッド内でメソッドを使う

メソッド内で他のメソッドを呼び出すにはthis.メソッド名()と書きます。

今回は例として、先ほどの例に新しいメソッドを追加して、それを呼び出してみます。

index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
    }
    //新しいメソッドを定義
    introduceAdd() {
        console.log('FC Barcelonaの選手紹介');
        //メソッド内でメソッドを使う
        this.introduce();
    }
}

const barca = new Barcelona('Messi', 10);
//新しく作成したメソッドを呼び出す
barca.introduceAdd();
// FC Barcelonaの選手紹介
// FC Barcelonaの背番号10はMessiです

追加したコードを見ていきます。

introduceAddというメソッドを定義しています。introduceAdd内では、introduceメソッドを呼び出すために、this.introduce();としています。

introduceAddメソッドを呼び出して実行すると、しっかりと処理が実行されました。

クラス式

クラス式」とは、クラスを定義するもう一つの方法です。

クラス式を用いると、名前なしでクラスを定義することができます。

書き方は関数式のように、定数や変数にクラスを代入します。

これまでのコードをクラス式で書き換えてみましょう。

index.js
//クラス式でクラスを定義
let Barcelona = class {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
    }
}

const barca = new Barcelona('Messi', 10);
console.log(barca.name);

コードの変わった部分をみてみます。

これまでclass Barcelona {}と定義していたところを、let Barcelona = class { }と書き換えました。

その他は変更はありません。

コードは問題なく実行されます。

クラスの継承

継承」とは、すでにあるクラスをもとに新しくクラスを作成する方法のことです。
継承元になるクラスを親クラス、新しく作成するクラスを子クラスと呼び、class 子クラス名 extends 親クラス { }と書きます。

Barcelonaクラスを継承してForwardクラスを作成してみます。

index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
    }
}

//クラスを継承
class Forward extends Barcelona {

}

//インスタンスを生成
const forward = new Forward('Messi', 10);

//継承したメソッドを呼び出す
forward.introduce();
//FC Barcelonaの背番号10はMessiです

コードの変更部分を見てみましょう。

class Forward extends Barcelona { }でクラスを継承しています。定義したクラスの中には何も処理を書いていません。

その後、Forwardクラスからforwardインスタンスを生成しています。この時、親クラスに書かれているconstructorでは引数を2つ受け取るので、インスタンスを生成する際にも2つの引数を渡します。

forward.introduce();で継承したメソッドを呼び出しています。Forwardクラスの中では、何も処理を記述していませんが、Barcelonaクラスを継承しているので、introduceメソッドを使うことができます。

このように、作成した子クラスは、親クラスの全ての機能を引き継いでいるため、親クラスに定義されているメソッドを使用することができます。

メソッドの追加

継承して作成した子クラスにも、独自のソッドを追加することができます。
子クラスで定義した独自のメソッドは、親クラスから呼び出すことは出来ないので注意しましょう。

先ほどの例で、子クラスにメソッドを追加してみます。

index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
    }
}

class Forward extends Barcelona {
    //独自のメソッドを追加
    introducePosition() {
        console.log('ポジションはFWです');
    }
}

//インスタンスを生成しメソッドを呼び出す
const forward = new Forward('Messi', 10);
forward.introducePosition();
//ポジションはFWです

コードを見てみましょう。

Barcelonaクラスを継承したForwardクラスの中でintroducePosition() { }というメソッドを記述しています。このメソッドでは、ポジション紹介をコンソールに出力する処理を書いています。

先ほどの例と同じくforwardインスタンスを生成します。

最後に、forward.introducePosition();でメソッドを呼び出します。すると、メソッドの処理が実行され、コンソールに文章が出力されます。

オーバーライド

メソッドのオーバーライド

子クラスにすでに親クラスに存在するメソッドと同じ名前のメソッドを定義すると、子クラスのメソッドが優先して使用されます。
これを「オーバーライド」と呼び、子クラスのメソッドが親クラスのメソッドを上書きしています。

先ほどの例で、introduceメソッドをオーバーライドしてみます。

index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
    }
}

class Forward extends Barcelona {
    //introduceメソッドをオーバーライド
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
        //introducePositionメソッドを追加
        this.introducePosition();
    }
    introducePosition() {
        console.log('ポジションはFWです');
    }
}

//インスタンスを生成しメソッドを呼び出す
const forward = new Forward('Messi', 10);
forward.introduce();
// FC Barcelonaの背番号10はMessiです
// ポジションはFWです

変わった部分を見ると、子クラスであるForwardクラスの中で、親クラスの中でも定義されているintroduceメソッドの中身を書き換えています。

このメソッドをforward.introduce();として呼び出すと、追加で書いた処理も実行されていることがわかります。

今この状態では、Barcelonaクラスの中のintroduceメソッドとForwardクラスの中のintroduceメソッドが存在するわけですが、同じメソッドでも実行される処理は違うという状態になっています。

コンストラクタのオーバーライド

コンストラクタもオーバーライドすることができます。
オーバーライドすることで、子クラスにプロパティを追加したりすることができます。

子クラスでコンストラクタをオーバーライドする際は、1行目にsuper()と記述します。
このsuper()の部分が親クラスのコンストラクタを呼び出しています。

そのため、親クラスのコンストラクタが引数を受け取る場合は、superの後ろの( )の中に引数を渡す必要があります。

これまでの例に、positionプロパティを追加してみます。それに伴い、メソッドの内容も少し書き換えます。

index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
    }
}

class Forward extends Barcelona {
    //コンストラクタをオーバーライド
    constructor(name, uniformNumber) {
        super(name, uniformNumber);
        this.position = 'FW'
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}${this.name}です`);
        this.introducePosition();
    }

    //positionプロパティを使用
    introducePosition() {
        console.log(`ポジションは${this.position}です`);
    }
}

const forward = new Forward('Messi', 10);

forward.introduce();
// FC Barcelonaの背番号10はMessiです
// ポジションはFWです

//インスタンスを出力
console.log(forward);
//{ name: 'Messi', uniformNumber: 10, position: 'FW' }

変更部分をみてみます。

Forwardクラスにコンストラクタを追加しました。

コンストラクタの中では、まずsuper(name, uniformNumber);と書くことで、親クラスのコンストラクタを呼び出しています。
これに、this.introducePosition();と書くことで、プロパティと値を追加しています。

実際に生成したforwardインスタンスをconsole.log(forward);といて出力すると、子クラスで追加したプロパティと値が追加されたオブジェクトが表示されます。

まとめ

今回は以上となります。
コードが長くなってくると混乱してくるので、少しずつ理解していきましょう。

最後まで読んでいただきありがとうございました。ではまた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初投稿】売れた商品に対して、SoldOutの文字を出力する

条件式でexists?メソッドを使用し、指定したテーブル内のカラム名(item_id)と売れた商品(item.id)が一致したら商品にSoldOutの文字を出力するコードを書きました。

qiita.rb
<% if Order.exists?(item_id: [item.id]) %> 
  <div class='sold-out'>
    <span>Sold Out!!</span>
  </div> 
<% end %>

私のコードの書き方で当たり前なことかもしれませんが、こういう動作をさせたいといいう目的がある場合、今回の動作であれば売れた商品にSoldOutを表示させたいという目的がありました。

その目的に対して、exists?メソッドはいきなり見つけたわけではなく、まず商品を購入してitem_idを商品情報と一緒にorderテーブル保存しているのでitem_idがorderテーブルにあることに気が付きます。

商品のid、item_idを見境なしに判別してしまうのは違うので、orderテーブルにitem_idが存在した場合トップページに存在するitem_idとorderテーブルの中のitem_idを判別してくれればうまくいきそうだなと考え、トップページ商品id、item_idとorderテーブルのitem_idと見比べたいなと思いexists?メソッドにたどり着きました。

文章で書くときはなんとなく順番通り書きましたが、考えている時は初めにorderテーブルのitem_idと見比べるのではないかと思い付きまずさかのぼって考え、答えに至りました。

プログラミングで目的の動作を考える時は、抽象化された目的に対し細かくプロセスを考えることが大切だと分かりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DOM操作とは?【JavaScript】初心者の疑問

どうも、三町哲平です!

以前作成した【JavaScript】分からない部分まとめてみたら最強だった件 - Qiitaの記事は、現在(2020/11/14 時点)なんと25,000PVを超えました!

本当に読んでくれた皆様には、感謝、感謝なのですが、実際私は、確かにJavaScriptの分からない部分はまとめたのですが、JavaScriptに関しては依然素人も素人...

もう超ド素人な訳なんです。

そんな私のJavaScriptの謎の中にDOM操作というものがあります。

JavaScriptの特徴の一つにDOM操作がありますよ!なんて言われても超ド素人からすれば、そのDOMが分からんよ...てな話

では、そのDOMについてのお話。

まずは、ググってみる

Google先生に頼ってみますか。

「ドム とは」

スクリーンショット 2020-11-12 2.17.59.png

え〜っと...

ドム (DOM) は、「ガンダムシリーズ」に登場する架空の兵器。有人操縦式の人型ロボット兵器「モビルスーツ」 (MS) の一種。初出は、1979年放送のテレビアニメ『機動戦士ガンダム』。
引用元:ドム - Wikipedia

なるほど...DOMは、ガンダムに関係あるんですね...。

ドムとは、テレビアニメ『機動戦士ガンダム』に登場するモビルスーツである。
引用元:ドム (どむ)とは【ピクシブ百科事典】

ピクシブ百科事典に答えが載っていましたね。

DOM操作っていうのは、モビルスーツの操作の事だったんですね。

いや〜勉強になりましたね。

では、また!

............

......

...て、おいおい

おふざけは、ここまで

本題です。

ここで終わってたら、流石に怒られますからね...(汗)

「DOM とは」
スクリーンショット 2020-11-12 2.44.03.png

求めていた記事が沢山出てきましたね。

DOMとは?

  • Document 文書、資料
  • Object 物体、対象
  • Model 模型、原型

Google 翻訳では、上記のような日本語訳になります。

文書オブジェクトモデル(DOM)とは、HTML文書およびXML文書のためのアプリケーション=プログラミング=インターフェイス(API)である。これは、文書の論理的構造や、文書へのアクセスや操作の方法を定義するものである。
引用元:DOMとは何か

なるほど...分からん

そんなあなたに↓

オブジェクトのような感じで、HTMLのタグなどにアクセスして、CSSを追加したり、あるいはタグを追加したり・削除したりすることが出来る形のこと
引用元:JavaScriptでDOMを操作する方法【初心者向け】 | TechAcademyマガジン

初心者なりに噛み砕いてみた

JavaScriptの特徴からDOM操作

ここでDOM操作を考えていく上で必要になるのが、JavaScriptって何ができるのかという事です。
JavaScriptと言えば、Node.jsによるバックエンド開発も有名ですが、やっぱりメインはフロントエンドに動きをつけてUI/UXの向上であり、
その時のJavaScriptを使って、HTML表示を変える処理こそが、DOM操作

では、実践で確認していきましょう!

HTMLを書いてみる

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1 id="title">アムロ・レイ</h1>
</body>

</html>

スクリーンショット 2020-11-12 16.35.22.png

このHTMLのアムロ・レイ表示をJavaScriptを用いて変更してみましょう!

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1 id="title">アムロ・レイ</h1>
</body>

<!-- 追加 -->
<script>
    const h1 = document.getElementById("title");
    console.log(h1);
    h1.textContent = "シャア・アズナブル";
</script>

</html>

スクリーンショット 2020-11-12 16.43.20.png

h1は、アムロ・レイのままですが、JavaScriptの追加によりシャア・アズナブルになりました。
これが、DOM操作です。

何となくイメージは摑んでいただけましたでしょうか?

参考記事:JavaScriptでDOMを操作する方法【初心者向け】 | TechAcademyマガジン

さいごに

この記事は、上記でも書いてある通りDOM操作の何となくのイメージしか掴めないと思います。
詳しい方からすると何でDOM操作を解説する記事なのにツリー構造の説明がないの?ノードのノの字もないぞ!などといった意見はあると思いますが、対象は、そこでは無いことをご了承下さい。

さらにDOMについて気になった方は、個々人で調べてみることをお勧めします。
例えばこの記事とか↓が分かりやすいかと
JavaScript初心者でもすぐわかる!DOMとは何か?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで非同期通信で実装する投稿機能

はじめに

今回は非同期通信による投稿機能を実装していきます。
今回は非同期処理を行う事にフォーカスを当てるためすでに画面遷移ありの投稿機能が実装されている状態からのスタートとなります。
jsの理解のため、素のjsでの記述となります。
また初学者のため間違え等ありましたらご指摘頂けると幸いです。

手順1 remote: tureを追加

入力フォームにremote: tureを追記しましょう!

<%# タスク入力フォーム %>
<div class="task-contents">
    <%= form_with model: @category, id: 'new_tasks', class: 'new_tasks' do |f| %>
     <div class="task-field">
       <%= f.text_field :task %>
     </div>
     <div class="task-actions">
       <%= f.submit "+" %>
     </div>
    <% end %>
</div>

ここでどこにremote:tureがあるんだ?と思う方もいると思うのですがform_withを使うとデフォルトでajax通信を行う仕様になっているのでform_withを使う場合は特に記述は不要です。逆にajax通信にしない場合はlocal: trueにすると通常のリクエストになります。
form_forを使う方はremote: trueオプションを忘れずに追記してください。
心配な方は開発ツールなどで確認するとdata-remote="true"となっているはずなので確認してみると良いと思います。

手順2 コントローラーの記述

def create
      @category = Category.new(category_params)
      @category.save
      @categories = current_user.categories.all
end

一般的な保存機能を行う記述ですが@categories = current_user.categories.allの記述はこの後のjsに渡す情報として必要なので記述しています。

手順3 create.js.erbにJavaScriptの記述をする

remote: trueにより返却される内容がHTMLではなくアクション名.js.erbファイルが読み込まれるようになっているのでcreate.js.erbファイルを作成します

(function(){
  document.querySelector(".task-lists").innerHTML =  '<%= j(render 'categories/index', categories: @categories) %>'
  document.querySelector("#category_task").value = ''
})();

.innerHTMLでHTMLの中身を書き換えています。

'<%= j(render 'categories/index', categories: @categories) %>'
ここで@categoriesを渡すために手順2で定義しています。
document.querySelector("#category_task").value = ''
この記述により入力入力フォームを空にしています。

最後に

以上で非同期での投稿機能が完成です!
重要なのはajax通信のデータの流れを理解できればそれぞれの処理はシンプルなので理解しやすいかなと思います。
初学者のためもっと良い記述があればご教授頂けると幸いです。

非同期削除などの記事もよければどうぞ〜
https://qiita.com/shantianchengyilang316/items/10ab2d84f6cfcfd29def

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Medium Editorでフォントカラーを変更できる仕様にカスタマイズしてみた

はじめに

Railsに導入したMediumエディターにフォントカラーの編集機能を追加する方法を自分の備忘録として書いておきます。

環境

Rails6 (6.0.1)
ruby 2.7.0
jQuery(3.4.1)をインストール済み

参考

medium-editor support for font color?
https://github.com/Olgagr/vanilla-color-picker

MediumEditorとは?

有名ブログサービスのMediumで使われているWYSIWYGエディターです。Gemのmedium-editor-railsをインストールすることで簡単にMediumエディターをRailsアプリに導入することができます。

MediumEditorを適用させる

テキストエリアにeditableというクラスを付与させた上で、下記のスクリプトを実行するとMedium Editorが適用されます。

  var editor = new MediumEditor('.editable', {
    // placeholder の設定
    placeholder: {
      text: 'テキストを入力してください',
      hideOnClick: true
    }
  });

  $('.editable').mediumInsert({
    editor: editor
  });

※RailsにMediumEditorを導入する際はこちらの記事を参考にしました。

フォントカラーの編集機能を追加する方法

vanilla-color-pickerを利用して、機能拡張を行います。

ディレクトリ構成は以下になります。

ディレクトリ構成

 app/
  ├ 〜
  └ javascript/
    ├ packs/
    │  └ application.js
    ├ vanilla-color-picker.js
    ├ color_picker.js
    └ medium_editor.js

1. vanilla-color-picker.jsをjavascriptディレクトリの配下に置く

ファイルはこちらのファイルを利用しました。

2. color-picker.jsをjavascriptディレクトリに作成

このファイルでColorPickerExtension()を下記のように定義します。

color-picker.js
(function () {

    function ColorPickerExtension() {
        this.button = document.createElement('button');
        this.button.classList.add('medium-editor-action');
        this.button.classList.add('editor-color-picker');
        this.button.title = '文字色'
        this.button.innerHTML = '<i class="fa fa-paint-brush"></i>';
        this.button.onclick = this.onClick.bind(this);
    }

    /**
     * onClick
     * The click event handler that calls `insertHtmlAtCaret` method.
     *
     * @name onClick
     * @function
     */
    ColorPickerExtension.prototype.onClick = function (e) {
        e.preventDefault();
        e.stopPropagation();
        this.selectionState = this.base.exportSelection();
        // If no text selected, stop here.
        if (this.selectionState && (this.selectionState.end - this.selectionState.start === 0)) {
          return;
        }

        // 選択できるフォントカラーを指定します
        var pickerColors = [
          "#1abc9c",
          "#2ecc71",
          "#3498db",
          "#9b59b6",
          "#34495e",
          "#16a085",
          "#27ae60",
          "#2980b9",
          "#8e44ad",
          "#2c3e50",
          "#f1c40f",
          "#e67e22",
          "#e74c3c",
          "#bdc3c7",
          "#95a5a6",
          "#f39c12"
        ];

        var picker = vanillaColorPicker(this.document.querySelector(".medium-editor-toolbar-active .editor-color-picker").parentNode);
        picker.set("customColors", pickerColors);
        picker.set("positionOnTop");
        picker.openPicker();
        picker.on("colorChosen", function (color) {
          this.base.importSelection(this.selectionState);
          this.document.execCommand("styleWithCSS", false, true);
          this.document.execCommand("foreColor", false, color);
        }.bind(this));
    };

    /**
     * getButton
     * This function is called by the Medium Editor and returns the button that is
     * added in the toolbar
     *
     * @name getButton
     * @function
     * @return {HTMLButtonElement} The button that is attached in the Medium Editor
     * toolbar
     */
    ColorPickerExtension.prototype.getButton = function () {
        return this.button;
    };


    // windowオブジェクトに定義します
    window.ColorPickerExtension = ColorPickerExtension;
})();

3. MediumEditorのextensionsオプションにColorPickerExtension()を追加

2で定義したColorPickerExtension()をextensionsオプションに追加します。

  extensions: {
      "colorPicker": new ColorPickerExtension()
    }

これで"colorPicker"をtoolbarのbuttonsオプションに指定してあげるとMedium Editorでフォントカラーを編集できるボタンを表示させることができます。

  toolbar: {
        buttons: ['bold', 'italic', 'underline', 'anchor', 'colorPicker', 'indent', 'outdent'],
        diffLeft: 25,
        diffTop: -10,
    }

ファイル全体の内容は以下

medium_editor.js
  var editor = new MediumEditor('.editable', {
    // placeholder の設定
    placeholder: {
      text: 'テキストを入力してください',
      hideOnClick: true
    },
    toolbar: {
        buttons: ['bold', 'italic', 'underline', 'anchor', 'colorPicker', 'indent', 'outdent'],
        diffLeft: 25,
        diffTop: -10,
    },
    extensions: {
      "colorPicker": new ColorPickerExtension()
    }
  });

  $('.editable').mediumInsert({
    editor: editor
  });

4.application.jsを編集

この状態では、vanilla-color-picker.jsとcolor-picker.jsが読み込まれていない状態なのでapplication.jsに追記します。

application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("jquery")

//追加 ----
require("vanilla-color-picker.js")
require("color-picker.js")
// -------

require("medium_editor.js")


完成

これでMedium Editorでフォントカラーを編集できるようになります。
guide6.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript基礎知識#1

はじめに

この記事は私の脱コピペを目指すために書いている自分用のメモです。

※内容について、誤った知識やもっとわかりやすい表現等ございましたら、ご指摘いただけますと幸いです。

目次

・前提知識
・Javascriptとは
・実際にできること
・用意するツール
・記述方法(二種類)

前提知識

HTML および CSS の基本的な理解。

Javascriptとは

・ブラウザで実行され、ブラウザを操作できる。
・HTMLやCSSでは作れない動的なサイトを作ることができえる。
・HTMLやCSSをリアルタイムで書き換えることができる。

実際にできること

例)・写真スライドさせるスライダー
  ・入力欄のエラー表示
  ・テキストの書き換え

main.js
const para = document.querySelector('p');

para.addEventListener('click', updateName);

function updateName() {
  let name = prompt('名前を入力して下さい');
  para.textContent = 'Player 1: ' + name;
}

こんな感じのコード。理解できなくてよい。(MDN参照)

用意するツール

・ブラウザ(chrome, firefox, IE, Safariなどなんでもオッケー(筆者はChrome))
・テキストエディタ(VScode, Atom などなんでもオッケー(筆者はVScode))

記述方法

JavaScriptの記述方法は、次の2種類ある。

・HTMLファイルに直接記述する
・専用の別のファイルにJavaScriptのみ記述する(main.jsなど)

index.html
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <script>
      let x = 1;
      console.log(x);
    </script>
  </body>
</html>

↑HTMLファイルにscriptタグを書いて記述する。

index.html
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>
main.js
let x = 1;
console.log(x);

↑HTMLファイルとは別のmain.jsファイルを作り、HTMLファイルでmain.jsを読み込む。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript基本文法【変数】

はじめに

現在、JavaScriptの独学をしています。

復習を兼ねて、今回は変数についてまとめます。

私と同じようにJavaScript初心者の方の参考になればと思います。

変数

変数の宣言方法は3種類あります。

const 変数名 = 初期値;
let 変数名 = 初期値;
var 変数名 = 初期値;

この3種類には以下のような違いがあります。

  • constの特徴
    再代入できない、再定義できない。

  • letの特徴
    再代入できる、再定義できない。

  • varの特徴
    再代入できる、再定義できる。

基本はconstだけを使い、それだけでは対応できないときにletを使います。

varは再代入と再定義ができてしまい、不具合の原因になるので、使わないようにしましょう。

また、JavaScriptではキャメルケースで変数名をつけます。

const constNumber = 100;

これはどの宣言方法でも同じです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブックマークレット でcookie情報をアラート

この記事は

chrome のブックマークで、cookieの情報をalertする方法を解説します。

なぜ?

cookie情報を確認する時、いちいちF12開発者ツールを起動して、目的のcookie を取得するのが面倒だったので、
ブックマークからササっと確認したかった。

環境

chrom 86.0.4240

コード

"SID"という名前のcookieを取得する例です。

コード
javascript:(function(){
    var cookieItem = document.cookie.split(";");
    for (i = 0; i < cookieItem.length; i++) {
      if (cookieItem[i].match(/SID/)) {
          alert(cookieItem[i]);
      }
    }
})();

コード解説

解説1
    var cookieItem = document.cookie.split(";");

document.cookieで、cookie情報をすべて取得します。
また、cookieは「A=a;B=b;C=c;」の形なので、「;」で分割して配列に格納します。

配列
-要素1:A=a;
-要素2:B=b;
-要素3:C=c;

解説2
    for (i = 0; i < cookieItem.length; i++) {
      if (cookieItem[i].match(/SID/)) {
          alert(cookieItem[i]);
      }

さきほどの配列の中から、"SID"の文字列を探し、アラートします。

動かし方

1.ブックマークに登録
chromeのブックマーク機能で、「URL」欄に上記コードを入れます。「名前」は何でも構いません。
image.png

2.ブックマークを選択

3.ダイアログがでてくれば成功です。
image.png

まとめ

SIDの部分を書き換えて、web画面のテストなどにお使いください。
cookieを書き換える事もできるようなので、そちらを駆使すれば応用が広がるかもしれません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【webpack】webpack設定を一からまとめてみる②

webpack学習の備忘録。
【webpack】webpack設定を一からまとめてみる① の続き。
前記事では基本設定について書いたので、本記事ではもう少し実践的な使い方についてをまとめたいと思う。

・開発と本番で設定ファイルを分ける
・ファイルの最適化を行う

開発と本番で設定ファイルを分ける

開発用であれば、早くビルド出来るよう考慮したり、
本番用であれば、圧縮や出力ファイルの最適化を優先したりなどが必要になってきます。
なので、使用するプラグインなども違って来るので、自ずとファイルを分けておくと管理がしやすくなる。
スクリーンショット 2020-11-14 14.00.51.png
基本は共通設定ファイル、開発用、本番用とそれぞれ用意する形。

まず、共通設定ファイルを読み込む為のプラグインをインストールする。

npm add webpack-merge --dev

開発用ファイル

webpack.dev.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpackMerge = require('webpack-merge'); // これでwebpackの設定をマージする
const commonConf = require('./webpack.common'); // 共通設定ファイルを読み込む
const outputFile = '[name]'; // 出力ファイルを変数化 
const assetFile = '[name]'; // 上に同じく

// マージの第一引数に読み込む共通設定ファイル
module.exports = () => webpackMerge.merge(commonConf({outputFile, assetFile}), {
  mode: 'development', 
  devtool: 'source-map',
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      inject: 'body' 
    }),
  ],
});

デバッグがしやすいようにdevtool: 'source-map'でソースマップを出力するようにしておきます。

本番用ファイル

webpack.prod.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpackMerge = require('webpack-merge'); 
const commonConf = require('./webpack.common'); 
const outputFile = '[name].[chunkhash]'; // 本番ではファイルにハッシュをつける
const assetFile = '[contenthash]'; // 上に同じく

module.exports = () => webpackMerge.merge(commonConf({outputFile, assetFile}), {
  mode: 'production', 
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      inject: 'body'
    }),
  ],
});

共通設定ファイル

webpack.common.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 

// 変数化した部分をパラメーターとして渡す
module.exports = ({outputFile, assetFile}) => ({
  entry: {app: './src/app.js', sub: './src/sub.js'}, 
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: `${outputFile}.js` // 開発と本番で出力結果を変えたい部分を変数化
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: `${outputFile}.css`,
    }),
  ],

// 以下略

あとはこちらの記事のようにpackage.jsonscriptsコマンド を定義しておくと実行する時に楽になる。

ファイルの最適化を行う

所謂、圧縮、軽量化を行うminify が出来るようにする。
cssのminifyにはoptimize-css-assets-webpack-pluginを新たに導入し、htmlは既に導入済みのhtml-webpack-pluginに設定を追加する事で実現が可能となっている。

javascriptのminifyにはTerserPluginと言うプラグインを用いるが、productionモードで実行する際にはデフォルトで使用されているプラグインとなっている。しかし、cssのminifyで新たに導入するプラグインを設定を書く為にoptimizationを設定すると、デフィルトの設定が無効化されてしまう為、別途設定を記述してやる必要があるそうな。

と言うわけで必要なプラグインをインストールする

npm add optimize-css-assets-webpack-plugin terser-webpack-plugin --dev

本番用ファイルにプラグインの読み込みと設定を追記する

webpack.prod.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin'); // javascriptのminifyで使う
const OptimizeCssPlugin = require('optimize-css-assets-webpack-plugin'); // cssのminifyで使う
const webpackMerge = require('webpack-merge'); 
const commonConf = require('./webpack.common'); 
const outputFile = '[name].[chunkhash]';
const assetFile = '[contenthash]';

module.exports = () => webpackMerge.merge(commonConf({outputFile, assetFile}), {
  mode: 'production', 
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      inject: 'body', 
      // htmlのminify設定を記述(デフォルトを上書き)
      minify: {
          collapseWhitespace: true, // 空白を取り除く
          removeComments: true, // コメントを取り除く
          removeRedundantAttributes: true, // 不要なAttributesを取り除く
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true,
          useShortDoctype: true
      }
      // chunkFilename: '[name].[hash].css'
    }),
  ],
  // 各プラグイン読み込み
  optimization: {
    minimizer: [
      new TerserPlugin(), 
      new OptimizeCssPlugin() 
    ]
  }
});

これでビルド時に出力されるファイルがmknify出来るようになる。
前記事でも触れたが、開発モード、本番モードのデフォルト設定については以下公式を参照。
Mode | webpack

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】getElementsByTagNameの結果をメソッドを使って配列にする方法

getElementsByTagNameした結果をfindしたかったのですが、そのままでは使えないんですね。
HTMLCollectionオブジェクトが返ってきました。

sliceを使って、配列にしてみました。

const tags = document.getElementsByTagName('script');
const targetTag = Array.prototype.slice.call(tags).find(c => c.src?.match(/test.com/)));

もしくは、Array.fromで、

const tags = document.getElementsByTagName('script');
const targetTag = Array.from(tags).find(c => c.src?.match(/test.com/)));

もしくは、スプレッド演算で、

const tags = document.getElementsByTagName('script');
const targetTag = [...tags].find(c => c.src?.match(/test.com/)));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js 再入門】 v-model を正しく理解して親子コンポーネント間のデータ伝播をマスターする

はじめに

Vue.js のコンポーネント間のデータのやりとりについて、親コンポーネントから子コンポーネントへデータを渡すには、v-bind 属性と props オプションを利用します。これにより親コンポーネント側でデータを変更すれば、子コンポーネントの props 経由で渡されたデータも変更されます。しかし、子コンポーネント側で props 経由で渡されたデータを直接変更することは非推奨です。しかし、子コンポーネントの変更を親コンポーネントへ反映したい場合があります。たとえば、再利用性のある入力フォームのコンポーネントを親子間でやりとりするケースです。この問題を解決するには v-model を正しく理解する必要があります。

御復習い(おさらい)

v-model は双方向データバインディングを実現するディレクティブです。双方向データバインディングとは、ビューで変更があった場合、その値を Vue インスタンスのデータとして更新します。逆に Vue インスタンスのデータに変更があった場合はビューを再レンダリングします。

<input v-model="message" />

v-model は以下のコードの糖衣構文になります。

糖衣構文
<input
  :value="message"
  @input="message = $event.target.value"
/>

さらにコンポーネントの v-model の糖衣構文は以下のようになります。

コンポーネントの糖衣構文
<custom-input
  :value="message"
  @input="message = $event"
></custom-input>

本題

子コンポーネントの変更を親コンポーネントへ反映する方法を紹介します。まずは親コンポーネントについて説明します。まずは、v-bind 属性経由で子コンポーネントにデータ(message)を渡すための準備をします。props は親コンポーネントの v-bind 属性経由で渡します。

親コンポーネント
<!-- コードは一部省略しています。 -->
<custom-input :value="message"></custom-input>

<script>
export default {
  data() {
    return {
      message: "",
    };
  },
};
</script>

v-bind 属性経由で渡されたデータは子コンポーネントの props オプションで定義します。

子コンポーネント
<script>
// props オプションの定義
export default {
  props: {
    value: {
      type: String,
    },
  },
};
</script>

上記のように props オプションを定義すれば、コンポーネントをインスタンス化したときにオブジェクトのプロパティとして利用できます。つまり、data と同様にテンプレート中で展開できます。それでは子コンポーネントの value 属性に props で定義したvaluev-bind 属性でバインディングしましょう。

子コンポーネント
<input :value="value" />

子コンポーネントから親コンポーネントへの通信では、$emit メソッドでイベントを送出します。それでは子コンポーネントの入力イベントを親コンポーネントへ通信するための準備をしましょう。

子コンポーネント
<input
  :value="value"
  @input="$emit('input', $event.target.value)"
/>

input イベントをハンドリングして、$emit メソッドで親コンポーネントにイベントを送出します。$emit メソッドの第 2 引数には子コンポーネントで入力された値を指定します。これにより親コンポーネントの v-on ディレクティブを使ってイベントを受け取る際、子コンポーネントで入力された値を取得できます。

親コンポーネント
<custom-input
  :value="message"
  @input="message = $event"
></custom-input>

親コンポーネント側でも input イベントをハンドリングして、子コンポーネントで入力された値を親コンポーネントで管理している message に代入します。これで子コンポーネントの変更を親コンポーネントへ反映できます。しかし、このままでは少し冗長なのでもう少しスッキリした書き方があります。それが v-model です。v-model をカスタムコンポーネントで利用する場合、上記の糖衣構文になります。

親コンポーネント
<custom-input v-model="message"></custom-input>

ちなみに子コンポーネントでも同様に v-model を利用したい場合は、算出プロパティの getter 関数と setter 関数を利用します。算出プロパティはデフォルトでは getter 関数のみですが、必要があれば setter 関数も使えます。

子コンポーネント
<!-- v-model を利用した場合 -->
<template>
  <input v-model="inputedValue" />
</template>

<script>
export default {
  props: {
    value: {
      type: String,
    },
  },
  computed: {
    inputedValue: {
      get() {
        return this.value;
      },
      set(newValue) {
        this.$emit("input", newValue);
      },
    },
  },
};
</script>

input イベントでハンドリングして、v-bind 属性でバインディングした value 属性を setter 関数の引数に渡し、$emit メソッドで親コンポーネントに入力されたデータを伝播します。いかがでしょうか。どちらも v-model を正しく理解していなければ一見なにをやっているのか把握しにくいかと思います。v-model に限らず正しく仕様を理解することは重要です。当記事の内容がどなたかの一助になれば幸甚です。

サンプルコード

▼ v-model を利用しないサンプルコード
Edit 【Vue.js 再入門】v-model を正しく理解して親子コンポーネント間のデータ伝播をマスターする
▼ v-model を利用したサンプルコード
Edit 【Vue.js 再入門】v-model を正しく理解して親子コンポーネント間のデータ伝播をマスターする(v-model ver.)

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

簡易的にヒートマップを導入できるブックマークレットを作ってみた

入力フォームのユーザビリティを検証するのに、無料で簡単に導入できるヒートマップツールがないか探してみたのですが、そういうのはなかったので(chromeの機能拡張でPage Analyticsというのがありますが、ヒートマップとは違うので)自分でブックマークレットを作ってみました。
アドレスバー直打ちじゃなく、ブックマークに登録すると動きます。

javascript:b=document.body;r=25;window.onclick=function(){d=document.createElement('div');s=d.style;s.width=r*2+'px';s.height=r*2+'px';s.backgroundColor='red';s.position='absolute';s.left=event.pageX-r+'px';s.top=event.pageY-r+'px';s.opacity=0.04;s.borderRadius=r*2+'px';s.pointerEvents='none';b.appendChild(d);}

ヒートマップを使いたいページでブックマークレットを適用すると、画面上のクリックした箇所に赤い半透明のドットがプロットされ、同じ箇所にプロットされると濃くなっていくので、クリックの位置と回数を視覚的に捉えることができます。

heatmap_sample.png

ユーザーの行動を分析するために本格的なヒートマップサービスを導入するほどじゃないけれども、開発者がヒューリスティックかつ簡易的にユーザビリティを検証したいという分には、とりあえずこれで十分かなと思います。

ブックマークレットということで、画面そのものが同期で更新されるとヒートマップが解除されてしまうので、非同期で遷移していく画面などで活用できるんじゃないかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

簡易的にヒートマップを適用できるブックマークレットを作ってみた

入力フォームのユーザビリティを検証するのに、アドオンで簡単にヒートマップを適用できる無料ツールがないか探してみたのですが、そういうのはなさそうだったので(chrome機能拡張のPage Analyticsはヒートマップとは違うし、heatmap.jsは仕込まないといけないので)自分でブックマークレットを作ってみました。
(アドレスバー直打ちじゃなく、ブックマークに登録すると動きます)

javascript:b=document.body;r=25;window.onclick=function(){d=document.createElement('div');s=d.style;s.width=r*2+'px';s.height=r*2+'px';s.backgroundColor='red';s.position='absolute';s.left=event.pageX-r+'px';s.top=event.pageY-r+'px';s.opacity=0.04;s.borderRadius=r*2+'px';s.pointerEvents='none';b.appendChild(d);}

ヒートマップを使いたいページでブックマークレットを適用すると、画面上のクリックした箇所に赤い半透明のドットがプロットされ、同じ箇所にプロットされると濃くなっていくので、クリックの位置と回数を視覚的に捉えることができます。

heatmap_sample.png

ヒートマップツールを仕込むほどじゃないけれども、アドオンで簡易的にユーザビリティを検証したいという分には、とりあえずこれで十分かなと思います。

ブックマークレットということで、画面そのものが同期で更新されるとヒートマップが解除されてしまうので、非同期で遷移していく画面などで活用できるんじゃないかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

簡易的なヒートマップをアドオンで適用できるブックマークレットを作ってみた

Webページのユーザビリティやユーザー行動を検証するのに、簡易的なヒートマップをコードに仕込むことなくアドオンで適用できる無料ツールがないか探してみたのですが、そういうのはなさそうだったので(Chrome機能拡張のPage Analyticsはヒートマップとは違うし、heatmap.jsはコードに仕込まないといけないので)自分でブックマークレットを作ってみました。
(アドレスバーにコピペではなく、ブックマークに登録すると動きます)

javascript:b=document.body;window.onclick=function(e){plot(25,'red',e,0.04)};window.addEventListener("mousemove",function(e){plot(10,'blue',e,0.01)});function plot(radius,color,event,opacity){d=document.createElement('div');r=radius;s=d.style;s.width=r*2+'px';s.height=r*2+'px';s.backgroundColor=color;s.position='absolute';s.left=event.pageX-r+'px';s.top=event.pageY-r+'px';s.opacity=opacity;s.borderRadius=r*2+'px';s.pointerEvents='none';b.appendChild(d);}

 
ヒートマップを適用したいページでブックマークレットを起動すると、画面上のクリックした箇所に赤い半透明のドットが、マウスの軌道に青い半透明のドットがプロットされ、同じ箇所にプロットされると濃くなっていくので、クリックとポインティグの位置と回数を視覚的に捉えることができます。
sample.png

ヒートマップツールをコードに仕込まず、アドオンで簡易的にユーザビリティやユーザー行動を検証したいという分には、とりあえずこれで十分かなと思います。

ブックマークレットということで、画面そのものが同期で更新されるとヒートマップが解除されてしまうので、画面遷移の少ないLPや、非同期で遷移していく画面などで活用できるんじゃないかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初学者向け JavaScript 『参照』について問題集

JavasSript「参照」について問題作ってみました

問題1

abはそれぞれコンソールにはなんと出力されるでしょう?

main.js
let a = {
  prop: 'Me on fleek!'
}

let b = a;
a.prop = '120% swag!';
console.log(a, b);

実行したいときはindex.htmlを作成してください

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="main.js"></script>
</body>
</html>

解答

オブジェクトを渡すときはオブジェクトへの参照がコピーされる。

{prop: "120% swag!"} {prop: "120% swag!"}

[メモ] JavaScript 参照

問題2

aarg2の値は?

main.js
let a = {
  prop: 0
}

function fn2(arg2) {
  arg2.prop = 1;
  console.log(a, arg2)
}

fn2(a);

解答

問題1を理解していると解けるはずです。

{prop: 1} {prop: 1}

問題3

以下はtruefalseか?

main.js
const a = {
  prop: 0
}

const b = {
  prop: 0
}

console.log(a === b);

解答

false

プリミティブ型では値の比較

オブジェクトは参照の比較

問題4

コンソールで表示される値は?

main.js
let obj = {
    prop1: 10
}
const obj2 = obj;
obj2.prop1 = 9;

function fn1(arg) {
    arg.prop2 = 8;
    return arg;
}
let obj3 = fn1(obj2);

console.log(obj);

解答

{prop1: 9, prop2: 8}

僕は理解するのに時間がかかった、、、

問題5

このままではコンソールに「10」と表示されます。
9」と表示したい場合どうすればいいでしょう?

main.js
let obj = {
    prop1: 10
}

function minus(obj, val) {
    let prop1 = obj.prop1;
    prop1 = prop1 - val;
}

minus(obj, 1);
console.log(obj.prop1);

解答

main.js
let obj = {
    prop1: 10
}

function minus(obj, val) {
    obj.prop1 = obj.prop1 - val;
}

minus(obj, 1);
console.log(obj.prop1);

問題にしてまとめるとすごく頭に残りました!

お付き合いありがとうございました。

参考

[JS]初級者から中級者になるためのJavaScriptメカニズム

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【nuxt.js】FontAwesomeを「軽く」使用する方法

nuxtアプリにFontAwesomeを導入する際、より「軽く」するためのメモになります。

前提

nuxtにFontAwesomeをインストールする方法は、公式ドキュメントに載っていますので割愛します。(要約して記事にしている方も多いですし)
参考:nuxt-fontawesome

FontAwesomeは重い

FontAwesomeは普通に実装すると結構重いです。そのため、軽くするための工夫が必要になります。
以下のように使用するアイコンの名前を直接指定すると、指定されたアイコンのみ読み込みます。

nuxt.config.js
fontawesome: {
    imports: [
      {
        set: '@fortawesome/free-solid-svg-icons',
        icons: ['faBars', ],//['fas']だと全て読み込んでしまう
      },
    ],
},

状況にもよるかと思いますが、私のプロダクトの場合は体感で3~4秒ほど初期ロードが短縮されました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブラウザから外部プログラムを起動するサーバー

概要

ブラウザから外部プログラムを起動することはブラウザ単体では(拡張機能を使っても)出来ないので、node.jsで外部プログラムを起動するサーバーを作ってみた。

ブックマーク機能を使えば簡単なランチャーにもなりそう。

ソース

Executer.js
var http = require('http');
var url = require('url');
var proc = require('child_process');

//コマンドラインからポート番号の設定、デフォルトは8888
var port = 8888
if(process.argv.length > 2) {
    port = process.argv[2];
}

//ヘッダHTMLの出力
var header = function(res) {
    res.write(`
<html>    
<head>
    <title>Node Executer</title>
</head>
<body>
    `);
};

//フォームHTMLの出力
var form = function(res) {
    res.write(`
    <form action="http://localhost:${port}" method="GET">
        <input type="text" name="command" value="" />
        <input type="submit" value="Execute!">
    </form>
    `);
};

//フッターHTMLの出力
var footer = function(res) {
    res.write(`
</body>
</html>
    `);
    res.end();
};

//サーバー起動
http.createServer(function (req, res) {
    if(req.method === 'GET'){
        //キャッシュしない設定でHTTPヘッダを出力
        res.writeHead(200, {'Content-Type': 'text/html', 'Cache-Control': 'private, no-cache, no-store, must-revalidate'});

        var query = url.parse(req.url, true).query;
        var command = '';
        var out = '';
        var iserr = false

        //HTTP Queryからコマンド文字列を取得
        if(typeof query.command !== 'undefined') {
            command = query.command;
        }

        //ヘッダを出力
        header(res, command);

        if(command.length > 0) {
            //クエリにコマンド文字列が設定されていればspawnで起動
            res.write('<pre id="output" style="color:red">');
            var spwn = proc.spawn(command, [], { shell: true, env: process.env });

            //spawnのエラーイベント・エラー出力があればそのままレスポンスとして出力
            spwn.on('error', (err) => {
                iserr = true;
                res.write(data);
                console.log(data);
            });
            spwn.stdout.on('data', (data) => {
                //標準出力は出力しない
                //res.write(data);
            });
            spwn.stderr.on('data', (data) => {
                iserr = true;
                res.write(data);
            });

            //2秒後にフッタを出力して切断(起動したアプリはそのまま)
            setTimeout(() => {
                res.write('</pre>');
                //エラーがなければそのまま前のページに戻る
                if(!iserr) res.write('<script>history.back();</script>');
                footer(res);
            }, 2000);
        } else {
            //コマンド文字列が設定されていなければフォーム・フッタを出力して切断
            form(res);
            footer(res);
        }
    } else {
        //GETメソッド以外は404エラーを返す
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.write('Not Found');
        res.end();
    }
}).listen(port, '127.0.0.1');

起動は

> node Executer.js 8888

http://localhost:8888で接続できる

終了はCtrl-Cで。

外部からサーバーを(強制)終了させる

ポート番号がわかっていればnetstatコマンドでポートを確保しているプロセス番号がわかるので、それを元にkillすることで正常終了させる仕組みがないサーバーでも外部からサーバーを強制終了させることができるらしい。
下記はWindowsが対象だが、たぶんLinuxでも同じようなことは出来ると思う。

参考:

portkill.bat
@echo off

set __KILLPID=
set __TEMPPID=

if "%1"=="" (
    echo 使い方: portkill.bat [ポート番号]
    goto end
)
for /F "delims=" %%i in ('netstat -aon ^| findstr /r 0\.[01]:%1[^^^^0-9]') do set __TEMPPID=%%i
for %%a in (%__TEMPPID%) do (
    set __KILLPID=%%a
)

if not defined __KILLPID (
    echo %1番ポートを待ち受けているプログラムが見つかりませんでした。
    goto end
)

echo %__KILLPID%
taskkill /pid %__KILLPID% /F

:end

set __KILLPID=
set __TEMPPID=

こんな感じのバッチファイルを作っておけば外部から任意のポートを待ち受けているプログラムをkillできる。

ブラウザと連動する

いろいろやり方があるような気がしないでもないですが、一番手っ取り早いのは

  • 先にサーバーを別コンソールで起動しておいて
  • コンソールでブラウザを起動すると終了するまでコンソールに制御が戻らないことを利用して終了を待ち
  • ブラウザが終了してコンソールに制御が戻ったらサーバーも終了させる

という動作をするバッチファイルを作ることでしょうか…

start.bat
@if not "%~0"=="%~dp0.\%~nx0" start /min cmd /c,"%~dp0.\%~nx0" %* & goto :eof
@echo off

start /min cmd /c node Executer.js 8888

C:\Program Files\Google\Chrome\Application\Chrome.exe 

call portkill.bat 8888

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript学習の記録(Part2)

今回で三回目の投稿になります。
簡潔に分かりやすくをテーマに書きました。
(なお、この記事はProgateのスライドを簡単にまとめたものです。)

繰り返し処理

while文

while文とは「~の間」という意味である。
「条件式がtrueの間{}内の処理を繰り返す」ことができる。

while文の書き方
while (条件式) {
  処理
}

(例) 変数numberが100以下のとき、1から100までの数字を出力する。
この場合は以下のようになる。

example1
//変数の定義
let number = 1;
//条件式
while (number <= 100) {
  console.tag(number);
//変数の更新
  number += 1;
}

1から100までの数字が出力される

実行結果
  1
  2
  .
  .
 99
100

while文を用いる場合は条件式の部分がいつかfalseにならなければいけない。
更新されないと同じ値を繰り返してしまう(無限ループ)
そのため、変数numberの値の更新を忘れないように書くこと!

for文

for文は、while文に比べてシンプルに書くことが特徴である。

for文の書き方
for (変数の定義;条件式;変数の更新) {
 処理
}
example2
for (let number = 1;number <= 100;number ++) {
  console.log(number);
}

また、for文とif文を組み合わせることもできる。

(例) 変数numberが100以下のとき、1から100までの数字を出力する。
  ただし、3の倍数のときは「3の倍数です。」と出力する。

example3
for (let number = 1;number <= 100;number ++) {
  if (number % 3 === 0) {
     console.log("3の倍数です");
  } else {
     console.log(number);
}
実行結果
1
2
3の倍数です
.
.
98
3の倍数です
100

参考

Progate > JavaScriptⅡ > 繰り返し文

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む