20210303のJavaScriptに関する記事は24件です。

LINEBotのリッチメニューエディタがないので自作した

LINEBotで、以下のようなリッチメニューを表示します。

image.png

リッチメニューは、トーク画面に表示されるメニューで、自分で自由に画像を作成できます。
HTMLでいうところのクリッカブルマップに相当し、自作する画像ファイルの範囲内で、四角形で範囲を指定し、そこをタッチしたときに特定の動作を行うことができます。
特定の動作として以下が可能です。

  • ポストバックアクション
  • メッセージアクション
  • URIアクション
  • 日時選択アクション

(参考) LINEリファレンス:リッチメニュー
https://developers.line.biz/ja/reference/messaging-api/#rich-menu

ソースコード一式をGitHubに上げておきました。

poruruba/LinebotRichmenu
 https://github.com/poruruba/LinebotRichmenu

トーク画面へのリッチメニューの設定

リッチメニューは、以下の3種類の設定箇所があり、いずれにも設定した場合、上の方が優先度が高いです。

  • Messaging APIで設定するユーザー単位のリッチメニュー
  • Messaging APIで設定するデフォルトのリッチメニュー
  • LINE Official Account Managerで設定するデフォルトのリッチメニュー

3つ目は、LINE Developersのページから設定可能です。

LINE Official Account Manager
 https://manager.line.biz/

アカウントを選択し、メッセージアイテム→リッチメッセージと選択すると、管理画面が表示されます。ただし、テンプレートが少なかったり、選択範囲が固定されていたりと、使いにくかったので、今回は使いません。今回は、前者2つのMessaging APIで設定するリッチメニューを扱います。

(i) Messaging APIで設定するデフォルトのリッチメニュー
これが一番簡単で、最初に一回だけAPIを呼び出せば完了です。設定したいリッチメニューの識別子(後述)を設定します。
すべての友達に同じリッチメニューが表示されます。

api\controllers\linebot-richmenu\index.js
app.client.setDefaultRichMenu(richmenu_id);

app.clientは、LINE Bot用の自作のライブラリ「line-utils.js」です。内部で以下のnpmモジュールを使っています。

line/line-bot-sdk-nodejs
 https://github.com/line/line-bot-sdk-nodejs

(ii) Messaging APIで設定するユーザー単位のリッチメニュー
ユーザごとに異なるリッチメニューを表示します。

api\controllers\linebot-richmenu\index.js
  await client.linkRichMenuToUser(event.source.userId, richmenu_id);

例えば、友達になったときに呼び出せばよいでしょう。
以下のような感じです。

api\controllers\linebot-richmenu\index.js
app.follow(async (event, client) =>{
  await client.linkRichMenuToUser(event.source.userId, richmenu_id);
});

以上により、トーク画面でリッチメニューが表示されるようになります。

リッチメニューの作成

で、じゃあリッチメニューはどうやって作成して、どうやってリッチメニューのIDを取得するのか。
それには、HTTP Post等で、設定します。

(参考) LINE Messaging APIリファレンス:リッチメニュー
https://developers.line.biz/ja/reference/messaging-api/#rich-menu

1点注意がありまして、CORS対応が必要なため、ブラウザのJavascriptから直接は呼び出せません。そこで、Node.jsサーバを立ち上げて、Javascriptからの要求を受け取って登録を行うようにします。

Node.js側での実装は、linebot sdkのnpmモジュールですでに関数化されているためそれを呼び出せばよいだけです。

①createRichMenu(richMenu: RichMenu): Promise
②setRichMenuImage(richMenuId: string, data: Buffer | Readable, contentType?: string): Promise

※他のAPIは以下を参照してください。
https://github.com/line/line-bot-sdk-nodejs/blob/next/docs/api-reference/client.md

①で、クリッカブルの位置情報を含めてリッチメニューとして登録し、リッチメニューIDを取得します。
ただし、このままでは画像ファイルは登録されていないため、続けて②で画像ファイルを登録して完了です。

ちなみに、一度登録したら変更できないため、修正後の新しいリッチメニューおよび画像ファイルを登録したのち、古いリッチメニューおよび画像ファイルを削除することになります。

画像ファイルのフォーマット(JpgかPNGか)を判別するためとStream操作のために、以下のnpmモジュールも使っています。

sindresorhus /file-type
 https://github.com/sindresorhus/file-type

paulja/memory-streams-js
 https://github.com/paulja/memory-streams-js

以下の部分を、環境に合わせて設定してください。LINE Developersコンソールから確認できます。(LINE Developers コンソール: https://developers.line.biz/ )

api\controllers\linebot-richmenu\index.js
const config = {
  channelAccessToken: '【チャネルアクセストークン(長期)】',
  channelSecret: '【チャネルシークレット】',
};

リッチメニューのクリッカブルマップに相当する部分は、以下に示すJSONフォーマットである必要があります。

Messaging APIリファレンス:リッチメニューオブジェクト
 https://developers.line.biz/ja/reference/messaging-api/#rich-menu-object

もろもろのリッチメニュー作成・登録作業をWebページから行えるようにエディタを作成しておきました。

image.png

画像ファイルをアップロードして可視化し、位置情報とアクション内容を追加して、最後にセットで登録することができます。登録が完了すると、リッチメニューIDが払い出されるので、それをLINEBotサーバに設定しましょう。

クリッカブルマップのように、X座標とY座標と、幅と高さを指定し、そこにクリックされたときのアクションを割り当てます。以下のアクションを選択して設定します。
・ポストバックアクション
・メッセージアクション
・URIアクション
・日時選択アクション
入力した範囲(X座標とY座標と幅と高さ)は、画像内に四角枠で示すようにしています。

また、何のリッチメニューを登録したか忘れてしまうので、すでに登録済みのリッチメニューや画像ファイルを参照できるようにしておきました。また、すでに登録済みのリッチメニューから複製して編集登録できるようにもしておきました。

以下の部分は、立ち上げたNode.jsサーバのURLに変えてください。

public\linebot-richmenu-edit\js\start.js
const base_url = "【Node.jsサーバのURL】";

終わりに

LINEBotに関する投稿に関しては、以下も参考にしてください。LINEボットサーバを立ち上げる手順を示しています。
 LINEボットを立ち上げるまで。LINEビーコンも。

以上

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

【monaca】JavaScriptでアプリの簡易パスロック機能を作る

monacaで作ったアプリにJavaScriptを使ってパスロックもどきを実装してみました。

こんなの↓

1.gif2.gif

自分で言うのもなんですが、なかなかサマになってませんか?(笑)

こちらがパスコードを設定/変更するための画面↓

4.gif

仕組みとしては、パスコード入力画面をモーダルとしてホーム画面に被せ、入力したパスコードがあらかじめローカルストレージに保存しておいたパスコードと一致したらモーダルを消す、という単純な機能です。

ただし、後で説明しますが、この方法はハイブリッドアプリでしか通用しません。モーダルを被せてるだけなので、Webアプリやサイトに使ってもあまりパスロックとしては機能しないのでご注意ください。

目次

1.完成形
2.パスロック設定メニュー
3.パスコード入力画面
4.パスロック設定メニュー解説
5.パスコード入力画面解説
6.注意点


1. 完成形

2. パスロック設定メニュー

まず完成形がこちら。
こちらパスロック設定メニューのコード。3個目のGIFのやつです

See the Pen passlock-menu by toshihide (@toshihide2000) on CodePen.

3. パスコード入力画面

See the Pen enterpass-modal by toshihide (@toshihide2000) on CodePen.

4. パスロック設定メニュー解説

新規パスコードを入力するフォーム

  <div  class="pass-container"id="newpass">
    <div>パスワードを設定/更新</div>
        <label>
          <input type="password"  maxlength="4" pattern="[1-9][0-9]*" inputmode="numeric" id="newpassword" required>
        </label>※数字4字
  </div>
  <div  class="pass-container"id="confirmation">
        <div>確認のためもう一度入力してください。</div>
        <label>
         <input type="password"  maxlength="4" pattern="[1-9][0-9]*" inputmode="numeric"id="pass-confirm" onchange="Varidate()" required>
        </label>
        <div  class="green"id="log"></div>
  </div>
  <div  class="modalbefore btn"id="pass-set">決定</div>

input type="password"だと英数字キーボードが出るのでinputmode="numeric"でテンキーが出るようにします。

 function Varidate(){
            var NewPass=document.getElementById("newpassword").value;
          var ConfirmPass=document.getElementById("pass-confirm").value;
            var Log= document.getElementById("log");
        if(NewPass===ConfirmPass){
         Log.innerHTML="一致";
         document.getElementById("pass-set").classList.remove("modalbefore");
         Log.classList.remove("red");
         Log.classList.add("green");
       }
    else{
         Log.innerHTML="不一致";
         Log.classList.remove("green");
         Log.classList.add("red");
       }
         };
  document.getElementById("pass-set").addEventListener("click",()=>{
       var savedpass=document.getElementById("newpassword").value;
       var Log= document.getElementById("log");
       if(savedpass.length<4){
           Log.innerHTML="パスワードは4字で設定してください";
           Log.classList.remove("green");
           Log.classList.add("red");
       }else{
           localStorage.setItem("kin-pass",savedpass);
           alert("パスワードを"+savedpass+"に設定しました");
       }
    });

パスコードがnullの状態で保存すると解除できなくなって詰むので、初期の状態ではパスコードを保存する「決定」ボタンはdisplay:none;であるmodalbeforeクラスをつけて消しておきます。
確認用フォームに入力し終わったタイミングで両方のフォームを参照し、互いのvalueが一致すればdisplay:none;を外し、保存できるようにします。

決定ボタンを押すとフォームのvalueが取得されます。パスコードが4文字未満だと保存せず、input要素にmaxlength="4"をつけているので、保存できるパスコードは4文字になります。保存できたらアラートを出してパスワードをユーザーに確認させます。

greenとかredとかいうクラスをつけ外ししていますが、ログの色を変えてるだけなので省いても大丈夫です。


    <div class="passlock-box" id="passlock-onoff">
         <i class="fas fa-lock-open" id="lock-open"></i>
        <i class="fas fa-lock none" id="lock"></i>
   <div class="switch">
            <div class="switchbtn" id="Switchbtn">
              <div class="btncircle" id="iosbtn" ></div>
              <div class="btncircle none" id="on"></div>
            </div>
          </div>
</div>
// <!-- ONにした時 -->
document.getElementById("iosbtn").addEventListener("click",()=>{
    var PASS=localStorage.getItem("kin-pass");
    if(PASS==null){
            alert("パスワードが設定されていないためロックをONにできません");
    }else{
        document.getElementById("iosbtn").classList.add("none");
        document.getElementById("on").classList.remove("none"); 
        document.getElementById("lock-open").classList.add("none");
        document.getElementById("lock").classList.remove("none");
        document.getElementById("Switchbtn").classList.add("swi-btn-active");
        localStorage.setItem("passlock",1);
    }
});
// <!-- OFFにした時 -->
document.getElementById("on").addEventListener("click",()=>{
  document.getElementById("iosbtn").classList.remove("none");
  document.getElementById("on").classList.add("none");
  document.getElementById("lock").classList.add("none");
  document.getElementById("lock-open").classList.remove("none");
  document.getElementById("Switchbtn").classList.remove("swi-btn-active");
  localStorage.setItem("passlock",0);
});


やたらとdocument.getElementById~~が続いてますが、これらは全てこれ↓です。パスロックの機能とは関係ない部分なので無視してもらって大丈夫です。

5.gif

大事なのは、パスロックをONにした時の localStorage.setItem("passlock",1);と、OFFにした時のlocalStorage.setItem("passlock",0);

初期状態ではもちろんパスロックはOFFになっていますが、パスロックをONにしたら、次にアプリを開いた時にはパスロックがONになっていないといけません。つまりパスロックがONなのかOFFなのかということをブラウザに記憶させ、次回読み込み時にはそれに基づいてパスロック入力モーダルを表示するかしないか分岐する必要があります。そこで、パスロックがONの状態を1、OFFの状態を0として、ON/OFFを切り替えた時に0か1かを保存しています。

5. パスコード入力画面解説

<!-- パスコード入力モーダル -->
<div class="off"id="enterpass-body">
 <div class="delay" id="enterpass-container">
  <div id="enterpass-text"> パスワード</div>
  <label>
    <input type="password" maxlength="4" pattern="[1-9][0-9]*" inputmode="numeric" required class="shake-2" id="enterpass" onchange="unlock()">
  </label>
 </div>
</div>

//パスコードが一致したら解除
function unlock(){
  var PassWord=localStorage.getItem("kin-pass");
  var Enterpass=document.getElementById("enterpass").value;
  if(PassWord===Enterpass){
   document.getElementById("enterpass-body").classList.add("off");
  }else{
   document.getElementById("enterpass").classList.toggle("shake");
   document.getElementById("enterpass").classList.toggle("shake-2");
  }
}
// ページの読み込み時 
window.addEventListener("load",()=>{
  var OnOff=localStorage.getItem("passlock")
  if(OnOff==1){
     document.getElementById("enterpass-body").classList.remove("off");
  }else{
     console.log("パスロックOFF");
  };
});

パスコード入力モーダルは、position:absolute;でビューポートいっぱいに画面に重ねられており、デフォルトでdisplay:none;がついています。アプリを開いたときに、ローカルストレージから0か1かを取得し、1なら(パスロックがONなら)display:none;を外してパスコード入力モーダルを表示させます。0ならdisplay:none;のままです。
パスロックがONの場合、先程のパスロックメニューで設定したパスコードと、フォームに入力されたパスコードが同じであれば、もう一度モーダルがdisplay:none;となり、パスロック解除となります。

以上が、JavaScriptでフロントだけで簡易パスロックを実装する方法です。

6. 注意点

注意したいのは、パスロックといえどモーダルで隠してるだけで、開発者ツールで普通に中身が見れてしまうのでWebアプリやサイトではパスロックとしては全く意味がないということです。ただ、今回のようにmonacaでアプリ化した場合には開発者ツールで覗くなんてことはできませんので、こういう場合に限っては、パスロックとしてそこそこ使えるんじゃないかと思います。


今回パスロックを実装したアプリ
※オ●禁アプリです
kinyoku-100px-icon.png
禁欲エボリューション

過去アプ↓
mem-100px-icon.png
文字数制限メモ

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

Vue + Vuetify + Storybook v6の環境を構築する

Vuetify環境にStorybookを導入する際、vue-cli-plugin-vuetify-storybookで素直にインストールするとStorybook v5が導入されてしまいます。
「どうしてもStorybook v6が使いたい.......! (新しいもの好き)」
とチャレンジしてみたところ苦戦したのでまとめてみました。

構築した環境

パッケージ バージョン
@vue/cli 4.5.11
vue 2.6.12
vuetify 2.4.5
@storybook/vue 6.1.20

Vue + Vuetify環境のプロジェクトの作成

Vuetifyのサイト(https://vuetifyjs.com/ja/getting-started/installation/ )を参考にVueプロジェクトを作成し、Vuetifyをインストールします。

# Vueプロジェクトの作成 (プリセットは「Default ([Vue 2] babel, eslint)」を選択しました)
$ vue create my-app
# 作成したプロジェクトへ移動
$ cd my-app
# Vuetifyをインストール (プリセットは「Default (recommended)」を選択しました)
$ vue add vuetify

現時点でVuetifyはVue 3には対応していません。
Vueプロジェクト作成の際にはVue 2を選択してください。

以下のコマンドを実行し、ブラウザで http://localhost:8080/ にアクセスし確認します。

$ npm run serve

無事Vue + Vuetifyの環境が構築できました。

Storybook v6のインストール

Storybookの公式サイト(https://storybook.js.org/docs/vue/get-started/install )を参考にStorybook v6をインストールします。

$ npx sb init

以下のコマンドを実行し、ブラウザで http://localhost:6006/ にアクセスし、Storybookが起動できることを確認します。

$ npm run storybook

Storybookの設定

まず、StorybookのWebpackを設定します。
Storybook v5ではwebpack.config.jsに設定を書いていたと思いますが、v6ではmain.jsに記載します。

.storybook/main.js
const path = require('path') 

module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  // webpackの設定
  webpackFinal: async (config, { configType }) => {
    // @がsrcディレクトリをさすように設定
    config.resolve.alias['@'] = path.resolve(__dirname, '..', 'src')
    // sass-loaderを設定
    config.module.rules.push({
      test: /\.sass$/,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader',
      ],
    })
    return config
  },
}

次にVuetifyプラグインファイルを少し書き換えて、optionsをexportするようにします。
(本番と同じvuetifyオプションをStorybookでも使用できるようにするためです)

src/plugins/vuetify.js
import Vue from 'vue';
import Vuetify from 'vuetify/lib/framework';

Vue.use(Vuetify);

// オプションをエクスポートします。といっても、今回は特にオプション設定していないので空オブジェクトです。
export const options = {}

export default new Vuetify(options);

Storybookの設定ファイルにvuetifyの設定を記載します。
Storybook v5ではconfig.jsに設定を記載していましたが、v6からはpreview.jsに記載することになりました。

.storybook/preview.js
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
import { options } from '@/plugins/vuetify.js'

// Vuetifyを設定し、vuetifyインスタンスを作成
Vue.use(Vuetify)
const vuetify = new Vuetify(options)

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
}

// decoratorsでvuetifyインスタンスをVueのオプションに登録
export const decorators = [
  (story, context) => {
    const wrapped = story(context)
    return Vue.extend({
      vuetify,
      components: { wrapped },
      template: `
        <v-app>
          <v-container fluid>
            <wrapped />
          </v-container>
        </v-app>
      `
    })
  },
]

Storyを書いてみる!

まずコンポーネントファイルを作ります。

src/components/MyButton/MyButton.vue
<template>
  <v-btn color="success" @click="$emit('click')">
    {{ text }}
  </v-btn>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    text: {
      type: String,
      default: 'ボタン',
    }
  }
}
</script>

次にストーリーを書きます。

src/components/MyButton/MyButton.stories.js
import vuetify from '@/plugins/vuetify'
import MyButton from './MyButton'

export default {
  component: MyButton,
  title: 'MyComponents/MyButton',
  argTypes: {
    click: { action: 'click '},
  }
}

const Template = (args, { argTypes }) => ({
  components: { MyButton },
  props: Object.keys(argTypes),
  // vuetifyインスタンスを登録する
  vuetify,
  template: '<my-button v-bind="$props" @click="click" />',
})

export const Default = Template.bind({})
Default.args = {
  text: 'できた!'
}

これらのファイルを作り、Storybookを起動し、http://localhost:6006/?path=/story/mycomponents-mybutton--default にアクセスすると、

Storybook_MyButton.png

できました!

参考文献

以下のWebサイトにとても助けられました。

最後に

今回.storybook/preview.jsだけでなく、MyButton.stories.jsのTemplateでもvuetifyインスタンスを与えています。
このようにしないと$vuetifyを使った際にエラーになる箇所があったためです。
もっとスマートな方法があればぜひ教えてください。

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

お名前ドットコムで登録したメールサーバーと「nodeemailer」を連携させてみた

最近気温の差が激しい。。。

今日はお名前ドットコムで登録した独自ドメインでメール送信を行いたいと思います。

早速説明に参りたいと思います。

はじめに

お名前ドットコムのドメイン登録のやり方は説明致しませんので、まだ登録していない方はご登録してから本記事をご覧ください。

また、メールを送信するためにはレンタルサーバーの登録が必要なので、こちらもご登録してから本記事をご覧ください。

nodeemailerでメール送信

メールアドレス登録

お名前ドットコムの「レンタルサーバー一覧」から登録したドメインでログインしてください。

image.png

このような画面になると思います。

「メール」をクリック

まずは、「メールアドレスを追加」でメールアドレスを追加してください。

無題.png

パスワードは忘れずに!この後使います!!

また、ここで登録したメールアドレスは「info@example.com」として説明していきます。

次に追加したメールアドレスの「詳細」へ。

ここで、受信サーバーやら送信サーバーの情報が表示されます。

この情報をもとにnodeemailerでメールを送信していきます。

transporterの作成

また、今回nodeemailerは「FirebaseFunctions」で行います。

index.js
const nodemailer = require('nodemailer')

exports.sendContactMail = functions.region('asia-northeast1').https.onCall((data, context) => {
  const mailAddress = `info@example.com`
  const password = `メール作成したときのパスワード`
  const transporter = nodemailer.createTransport({
    pool: true,
    host: 'mail**.onamae.ne.jp',
    port: 465,
    secure: true,
    auth: {
      user: mailAddress,
      pass: password
    }
  })
})

mailOptionsの作成

次に、誰にメールを送信するか、タイトルは何か、メールの内容を書いていきたいと思います。

index.js
const nodemailer = require('nodemailer')

exports.sendContactMail = functions.region('asia-northeast1').https.onCall((data, context) => {
  const mailAddress = `info@example.com`
  const password = `メール作成したときのパスワード`
  const transporter = nodemailer.createTransport({
    pool: true,
    host: 'mail**.onamae.ne.jp',
    port: 465,
    secure: true,
    auth: {
      user: mailAddress,
      pass: password
    }
  })

  // ここから追加
  const mailOptions = {
    from: mailAddress,
    to: data.to,
    subject: 'タイトル',
    html: `
      <p>${data.name} 様</p>
      <p>メールを受け取りました</p>
      <p>本メールへの返信によるご質問は承っておりません。</p>
      <p>今後とも何卒よろしくお願いいたします。</p>
      <p>ギルド・ザエモニア</p>
      `
  }

  return transporter.sendMail(mailOptions, (erro, info) => {
    if (erro) {
      return { 
        error: erro.toString(),
        message: 'To send mail is failed'
     }
    }
    return { 
      message: 'To send mail is successed'
    }
  })
})

このようにしてメールを送信する関数を作成することができました!

後は呼び出して送信するだけ!!

メール送信

const sendMailFunc = this.$function.httpsCallable('sendContactMail')

sendMailFunc({
  to: 'akki@gmail.com'
  name: 'akki'
})

これで実際にメールが届いたら成功です!

ただし、メールサーバーを作成したばかりだとメールが届かない場合があります。

なので、2日ほど開けてからまた実行して見て下さい!

以上、「お名前ドットコムで登録したメールサーバーと「nodeemailer」を連携させてみた」でした!

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

あと、最近「ココナラ」で環境構築のお手伝いをするサービスを始めました。

気になる方はぜひ一度ご相談ください!

Thank you for reading

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

「遺伝的アルゴリズムで最高にエッチな画像を生成しよう」比較用ブックマークレット

動機

遺伝的アルゴリズムで最高にエッチな画像を作ろう!に人類の希望を見出してしまったので,真剣にクリックがしたかった.2つの画像を目grepするのに飽きてしまったので,同じ位置で表示を切り替えて比較するツールを作った.

使い方

以下のリンクをブックマークバーにドラッグ&ドロップすると,ブックマークレットが登録される.これを上のリンク先で押すと,javascriptが実行され,画像が交互に入れ替わる.セキュリティが不安な人はリンクのアドレスをコピーするとコードが確認できる.
比較用bookmarklet

変換前のコード

jQueryを使用して,id=mainのdiv要素のoffsetをいじっている.Bookmarkletを作ろう(準備編)を参考に,以下のコードを変換してブックマークレットにした.

void((function(f){
    var script0 = document.getElementById('jquery');
    if(script0 != null){
        document.body.removeChild(script0);
    }
    var script = document.createElement('script');
    script.src = '//code.jquery.com/jquery-3.2.1.min.js';
    script.id = 'jquery';
    script.onload = function(){
      var $ = jQuery.noConflict(true);
      f($);
    };
    document.body.appendChild(script);
})(
function($, undefined){
    var main_offset = $('#main').offset();
    var next_left = main_offset.left;
    if(main_offset.left < 300){
        next_left = 420;
    }else{
        next_left = 160;
    }
    $('#main').offset({top: main_offset.top, left: next_left});
}))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「遺伝的アルゴリズムで最高にエッチな画像を作ろう!」比較用ブックマークレット

動機

遺伝的アルゴリズムで最高にエッチな画像を作ろう!に人類の希望を見出してしまったので,真剣にクリックがしたかった.2つの画像を目grepするのに飽きてしまったので,同じ位置で表示を切り替えて比較するツールを作った.

使い方

ブックマークレットを作成し,先ほどのページで押すと,javascriptが実行され,画像が交互に入れ替わる.セキュリティが不安な人はリンクのアドレスをコピーするとコードが確認できる.

ブックマークレットを作るには,ブラウザでお気に入り登録したページのurlを以下のコードに置き換える.

比較用bookmarklet
javascript:void%20function(b)%7Bvar%20a%3Ddocument.getElementById(%22jquery%22)%3Bnull!%3Da%26%26document.body.removeChild(a)%3Ba%3Ddocument.createElement(%22script%22)%3Ba.src%3D%22%2F%2Fcode.jquery.com%2Fjquery-3.2.1.min.js%22%3Ba.id%3D%22jquery%22%3Ba.onload%3Dfunction()%7Bvar%20c%3DjQuery.noConflict(!0)%3Bb(c)%7D%3Bdocument.body.appendChild(a)%7D(function(b%2Ca)%7Bvar%20c%3Db(%22%23main%22).offset()%3Bvar%20d%3D300%3Ec.left%3F420%3A160%3Bb(%22%23main%22).offset(%7Btop%3Ac.top%2Cleft%3Ad%7D)%7D)%3B

変換前のコード

jQueryを使用して,id=mainのdiv要素のoffsetをいじっている.Bookmarkletを作ろう(準備編)を参考に,以下のコードを変換してブックマークレットにした.

void((function(f){
    var script0 = document.getElementById('jquery');
    if(script0 != null){
        document.body.removeChild(script0);
    }
    var script = document.createElement('script');
    script.src = '//code.jquery.com/jquery-3.2.1.min.js';
    script.id = 'jquery';
    script.onload = function(){
      var $ = jQuery.noConflict(true);
      f($);
    };
    document.body.appendChild(script);
})(
function($, undefined){
    var main_offset = $('#main').offset();
    var next_left = main_offset.left;
    if(main_offset.left < 300){
        next_left = 420;
    }else{
        next_left = 160;
    }
    $('#main').offset({top: main_offset.top, left: next_left});
}))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

django-allauthでsignupするときにemailフィールドを表示しない方法

前提

認証周りにdjango-allauthを使用してるのだが、サインアップ(Signup)の際にemail入力のinputが出てくるのを消したい

ビフォーアフター

Before

image.png

After

image.png

結論

django-allauth側ではそのような設定はできなかった。
かなり時間をかけて、その設定を探したが、Emailのフィールドは消せない。
結局、django-allauthが用意しているテンプレートであるsignup.htmlにJavascriptを書きそのDOMをremoveすることで解決した。

方法

  1. templates/base.htmlに自作のJavascriptが書けるようにする
base.html
<body>
    {% include 'navbar.html' %}
    <div class="container-fluid">
        {% include 'messages.html' %}

        {% block content %}
        {% endblock content %}
    </div>

    <!-- Bootstrap JS -->
    {% include 'js.html' %}
    # ↓以下の2行を追加
    {% block myJs %} 
    {% endblock myJs %}
</body>

2.templatesフォルダにdjango-allauthが提供しているテンプレートをプロジェクトのフォルダに持ってくる。account/signup.htmlでjavascriptを設定

templates/account/signup.html
{% load i18n %}
{% load bootstrap4 %}

{% block head_title %}{% trans "Signup" %}{% endblock %}

{% block content %}
<h1>{% trans "Sign Up" %}</h1>

<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>

<form class="signup" id="signup_form" class="" method="post" action="{% url 'account_signup' %}">
  {% csrf_token %}
  {% bootstrap_form form layout='horizontal' %}
  {% if redirect_field_value %}
  <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
  {% endif %}
  <button type="submit">{% trans "Sign Up" %} &raquo;</button>
</form>

{% endblock %}

######## この部分を追加 ############
{% block myJs %}
<script>
  forms = document.getElementsByClassName('form-group row')
  forms[1].remove()
</script>

{% endblock myJs %}
######################################

こうすることによってform-groupとrowクラスが付いたものの中からemailのフォームだけを取り除くことができる。

cssでdisplay: none;をするよりクールだと思う笑

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

Chrome拡張機能の審査に引っかかったときの解決策の一つ

Notification()でデスクトップ通知を行うために、notifications権限を要求しているのに、

違反事項: 次の権限をリクエストしているが使用していない。
notifications

と言われてしまったときの解決策

Notification()を、chrome.notifications.create()に変える。

それだけ

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

以上、以下、未満、超過のネーミング

メディアクエリの変数を作るときなど、
以上、以下、未満、超過のネーミングについていつも悩むので今後はこれで。

ネーミング 意味
超過 gt greater than
以上 gte greater than or equal to
以下 lte less than or equal to
未満 lt less than

@sdkeiさんにコメントいただきました。

以上・以下も2文字で ge・le と書くことが多いようです。

ありがとうございます。

参考 :
Comparison Query Operators — MongoDB Manual
Bash Conditional Expressions (Bash Reference Manual)
関係演算子 - Wikipedia

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

JavaScript Primerを読んで学んだこと(2/3)

はじめに

この記事は第1回の記事に続き、第2回になります。
JavaScriptを体系的に学べるサイト「JavaScript Primer」を読み、個人的に重要だと感じたことをまとめました。
JavaScriptを学習する際に参考にしていただければ幸いです。

プロトタイプオブジェクト

JavaScriptにおけるほぼ全てのオブジェクトは、prototypeオブジェクトを継承しています。
prototypeオブジェクトは、全てのオブジェクトから利用できるメソッドなどを提供するベースオブジェクトと言えます。

例えば以下に示すtoStringメソッドはprototypeオブジェクトに定義されたメソッドなので、定義することなく全てのオブジェクトから呼び出すことができます。

const obj = {};
console.log(obj.toString()); // "[object Object]"

このようなprototypeオブジェクトに組み込まれているメソッドは、プロトタイプメソッドと呼ばれます。

配列

配列の作成とアクセス

配列の作成は配列リテラル( [ と ] )の中に要素をカンマ( , )区切りで記述するだけです。
また配列[インデックス]と記述することで、そのインデックスの要素を配列から読み取れます。

const array = ["one", "two", "three"];
console.log(array[0]); // => "one"

存在しないインデックスに対してアクセスした場合は、例外ではなくundefinedを返します。

const array = ["one", "two", "three"];
// `array`にはインデックスが100の要素は定義されていない
console.log(array[100]); // => undefined

要素の追加と削除

配列は可変長であるため、作成後の配列に対して要素を追加、削除できます。
配列の任意のインデックスの要素を追加、削除するにはspliceメソッドを利用できます。

const array = [];
array.splice(インデックス, 削除する要素数);
// 削除と同時に要素の追加もできる
array.splice(インデックス, 削除する要素数, ...追加する要素); // ...(Spread構文)については後述

以下に使用例を示します。

const array = ["a", "b", "c", "d"];
// 1番目から2つの要素("b", "c")を削除
array.splice(1, 2);
console.log(array); // => ["a", "d"]
// 2番目に新しい要素("e")を追加
array.splice(2, 0, "e");
console.log(array); // => ["a", "d", "e"]

結合

concatメソッドを使うことで、配列と配列や新しい要素を結合した新しい配列を作成できます。

const array = ["A", "B", "C"];
const newArray = array.concat(["D", "E"]);
console.log(newArray); // => ["A", "B", "C", "D", "E"]

const newArray = array.concat("新しい要素");
console.log(newArray); // => ["A", "B", "C", "D", "E", "新しい要素"]

また、...(Spread構文)を使うことで、配列リテラル中に既存の配列を展開できます。

const array = ["A", "B", "C"];
const newArray = ["X", ...array, "Z"];
console.log(newArray); // => ["X", "A", "B", "C", "Z"]

Spread構文はconcatメソッドとは異なり、配列リテラル中の任意の位置に配列を展開できます。

配列を反復処理するメソッド

forEachメソッド

forEachメソッドは、配列の情報を先頭から順番にコールバック関数へ渡し、反復処理を行うメソッドです。第1引数に要素、第2引数にインデックス、第3引数に配列そのものを渡します。

const array = ["a", "b", "c"];
array.forEach((currentValue, index, array) => {
    console.log(currentValue, index, array);
});
// コンソールの出力
// "a", 0, ["a", "b", "c"]
// "b", 1, ["a", "b", "c"]
// "c", 2, ["a", "b", "c"]

mapメソッド

mapメソッドは配列の要素を順番にコールバック関数へ渡し、コールバック関数が返した値から新しい配列を返すメソッドです。配列の各要素を加工したい場合に利用します。

第1引数に要素、第2引数にインデックス、第3引数に配列そのものを渡し、配列要素の先頭から順番に反復処理します。mapメソッドの返り値は、それぞれのコールバック関数が返した値を集めた新しい配列です。

const array = [1, 2, 3];
// 各要素に10を乗算した新しい配列を作成する
const newArray = array.map((currentValue, index, array) => {
    return currentValue * 10;
});
console.log(newArray); // => [10, 20, 30]

filterメソッド

filterメソッドは配列の要素を順番にコールバック関数へ渡し、コールバック関数がtrueを返した要素だけを集めた新しい配列を返すメソッドです。配列から不要な要素を取り除いた配列を作成したい場合に利用します。

第1引数に要素、第2引数にインデックス、第3引数に配列そのものを渡し、配列要素の先頭から順番に反復処理します。filterメソッドの返り値は、コールバック関数がtrueを返した要素だけを集めた新しい配列です。

const array = [1, 2, 3];
// 奇数の値を持つ要素だけを集めた配列を返す
const newArray = array.filter((currentValue, index, array) => {
    return currentValue % 2 === 1;
});
console.log(newArray); // => [1, 3]

文字列

文字列の結合

文字列を結合する簡単な方法は文字列結合演算子(+)を使う方法です。

const str = "a" + "b";
console.log(str); // => "ab"

const name = "JavaScript";
console.log("Hello " + name + "!");// => "Hello JavaScript!"

文字へのアクセス

文字列の特定の位置にある文字にはインデックスを指定してアクセスできます。

const str = "文字列";
// 配列と同じようにインデックスでアクセスできる
console.log(str[0]); // => "文"
console.log(str[1]); // => "字"
console.log(str[2]); // => "列"
// 42番目の要素は存在しない
console.log(str[42]); // => undefined

文字列の長さ

lengthプロパティは文字列の要素数を返します。

console.log("文字列".length); // => 3

エスケープシーケンス

文字列リテラル中にはそのままでは入力できない特殊な文字もあります。
エスケープシーケンスは、\と特定の文字を組み合わせることで、特殊文字を表現します。

  エスケープシーケンス         意味      
\' シングルクォート
\" ダブルクォート
\` バッククォート
\\ バックスラッシュ
\n 改行
\t タブ

ラッパーオブジェクト

JavaScriptのデータ型は、プリミティブ型( NumberStringなどの基本的な値の型 )とオブジェクトに分けられます。
いくつかのプリミティブ型のデータには、それぞれ対応するオブジェクトが存在し、プリミティブ型の値に対してのラッパーオブジェクトと呼びます。

ラッパーオブジェクトとプリミティブ型の対応は次のとおりです。

ラッパーオブジェクト プリミティブ型
Boolean 真偽値 trueやfalse
Number 数値 1や2
String 文字列 "文字列"
Symbol シンボル Symbol("説明")

たとえば文字列に対応するオブジェクトとして、Stringオブジェクトがあります。このStringオブジェクトをnewすることで、Stringオブジェクトのインスタンスを作ることができ、文字列を明示的にオブジェクトとして扱うことができます。

// "input value"の値をラップしたStringのインスタンスを生成
const str = new String("input value");
// StringのインスタンスメソッドであるtoUpperCaseを呼び出す
str.toUpperCase(); // => "INPUT VALUE"

JavaScriptでは、プリミティブ型の値に対してプロパティアクセスするとき、自動で対応するラッパーオブジェクトに変換されます。これにより、プリミティブ型の値がラッパーオブジェクトのインスタンスメソッドを呼び出せるようになります。

const str = "string";
// プリミティブ型の値に対してメソッド呼び出しを行う
str.toUpperCase();
// `str`へアクセスする際に"string"がラッパーオブジェクトへ変換され、
// ラッパーオブジェクトはStringのインスタンスなのでメソッドを呼び出せる
// つまり、上のコードは下のコードと同じ意味である
(new String(str)).toUpperCase();

リテラルを使ったプリミティブ型の文字列とラッパーオブジェクトを使った文字列オブジェクトを明示的に使い分ける利点はないため、常にリテラルを使うことが推奨されています。

スコープ

スコープとは変数の名前や関数などの参照できる範囲を決めるものです。スコープの中で定義された変数はスコープの内側でのみ参照でき、スコープの外側からは参照できません。

関数によるスコープ

次のコードでは、fn関数のブロック( { と } )内で変数xを定義しています。この変数xfn関数のスコープに定義されているため、fn関数の内側では参照できます。一方、fn関数の外側から変数xは参照できないためReferenceErrorが発生します。

function fn() {
    const x = 1;
    // fn関数のスコープ内から`x`は参照できる
    console.log(x); // => 1
}
fn();
// fn関数のスコープ外から`x`は参照できないためエラー
console.log(x); // => ReferenceError: x is not defined

ブロックスコープ

{}で囲んだ範囲をブロックと呼び、スコープを作成します。ブロック内で宣言された変数はスコープ内でのみ参照でき、スコープの外側からは参照できません。
ブロックによるスコープのことをブロックスコープと呼びます。

// ブロック内で定義した変数はスコープ内でのみ参照できる
{
    const x = 1;
    console.log(x); // => 1
}
// スコープの外から`x`を参照できないためエラー
console.log(x); // => ReferenceError: x is not defined

スコープチェーン

以下のように入れ子になったスコープを考えます。

{
    // OUTERブロックスコープ
    const x = "x";
    {
        // INNERブロックスコープからOUTERブロックスコープの変数を参照できる
        console.log(x); // => "x"
    }
}

上記の例では、INNERブロックスコープからOUTERブロックスコープの変数を参照できています。これは、参照したい変数(今回はx)を探索する際に次のようなステップを踏むからです。

  1. INNERブロックスコープに変数xがあるかを確認 => ない
  2. ひとつ外側のOUTERブロックスコープに変数xがあるかを確認 => ある

この内側から外側のスコープへと順番に変数が定義されているか探す仕組みのことをスコープチェーンと呼びます。

グローバルスコープ

プログラム直下( ブロックで囲まれていない一番外側の部分 )には、暗黙的なグローバルスコープと呼ばれるスコープが存在します。

// プログラム直下はグローバルスコープ
const x = "x";
console.log(x); // => "x"

グローバルスコープで定義した変数はグローバル変数と呼ばれ、グローバル変数はあらゆるスコープから参照できる変数となります。 なぜなら、スコープチェーンの仕組みにより、最終的にもっとも外側のグローバルスコープに定義されている変数を参照できるためです。

クロージャー

クロージャーとは、関数内から特定の変数を参照し続けることで、関数が状態を持てる仕組みのことを言います。

クロージャーはJavaScriptが持つ以下の2つの仕組みを利用しています。

  • ある変数がどの値を参照するかはコードを実行する前から決まっている( 静的スコープ )
  • あるデータがどこかから参照されている限りは、そのデータがメモリ解放されることはない( ガベージコレクション )

次の例ではcreateCounter関数が、関数内で定義したincrement関数を返しています。その返されたincrement関数をmyCounter変数に代入しています。このmyCounter変数を実行するたびに1, 2, 3と1ずつ増えた値を返しています。

// `increment`関数を定義して返す関数
function createCounter() {
    let count = 0;
    // `increment`関数は`count`変数を参照
    function increment() {
        count = count + 1;
        return count;
    }
    return increment;
}
// `myCounter`は`createCounter`が返した関数を参照
const myCounter = createCounter();
myCounter(); // => 1
myCounter(); // => 2
// 新しく`newCounter`を定義する
const newCounter = createCounter();
newCounter(); // => 1
newCounter(); // => 2
// `myCounter`と`newCounter`は別々の状態持っている
myCounter(); // => 3
newCounter(); // => 3

このように、まるで関数が状態(ここでは1ずつ増えるcountという値)を持っているように振る舞える仕組みの背景にはクロージャーがあります。

クラス

クラスの定義

クラスを定義するにはclass構文を使います。クラスの定義方法にはクラス宣言文とクラス式があります。

クラス宣言文ではclassキーワードを使い、classクラス名{ }のようにクラスの構造を定義できます。

class MyClass {
    constructor() {
        // コンストラクタ関数の処理
        // インスタンス化されるときに自動的に呼び出される
    }
}

もうひとつの定義方法であるクラス式は、クラスを値として定義する方法です。クラス式ではクラス名を省略できます。

const MyClass = class MyClass {
    constructor() {}
};

const AnonymousClass = class {
    constructor() {}
};

クラスは必ずコンストラクタを持ち、constructorという名前のメソッドとして定義します。コンストラクタとは、そのクラスからインスタンスを作成する際に、インスタンスに関する状態の初期化を行うメソッドです。constructorメソッドに定義した処理は、クラスをインスタンス化したときに自動的に呼び出されます。

クラスのインスタンス化

クラスのインスタンス化には、new演算子を使用します。

class MyClass {
}
// `MyClass`をインスタンス化する
const myClass = new MyClass();

クラスにおけるメソッド

クラスにおけるメソッドは、クラスの直下に定義した場合とコンストラクタ内で定義した場合で挙動が異なります。

クラスの直下に定義したメソッドは、クラスの各インスタンスから共有されるメソッドとなります。このインスタンス間で共有されるメソッドのことをプロトタイプメソッドと呼びます。

一方コンストラクタ内で定義したメソッドは、インスタンス作成時に毎回新しく定義されるため、インスタンスによって参照先が異なります。

class Counter {
    constructor() {
        // `this`は`Counter`のインスタンスを参照する
        this.count = 0;
        this.InnerIncrement(){
            this.count++;
        }
    }
    outerIncrement() {
        this.count++;
    }
}
const counterA = new Counter();
const counterB = new Counter();
// クラスの直下に定義したメソッドは共有されている(同じ関数を参照している)
console.log(counterA.outerIncrement === counterB.outerIncrement); // => true
//コンストラクタ内で定義したメソッドは参照先は異なる
console.log(counterA.innerIncrement === counterB.innerIncrement); // => false

継承

extendsキーワードを使うことで既存のクラスを継承できます。継承とは、クラスの構造や機能を引き継いだ新しいクラスを定義することです。

また、extendsを使って定義した子クラスから親クラスを参照するにはsuperというキーワードを利用します。 もっともシンプルなsuperを使う例としてコンストラクタの処理があります。

// 親クラス
class Parent {
    constructor(...args) {
        console.log("Parentコンストラクタの処理", ...args);
    }
}
// Parentを継承したChildクラスの定義
class Child extends Parent {
    constructor(...args) {
        // Parentのコンストラクタ処理を呼び出す
        super(...args);
        console.log("Childコンストラクタの処理", ...args);
    }
}
const child = new Child("引数1", "引数2");
// "Parentコンストラクタの処理", "引数1", "引数2"
// "Childコンストラクタの処理", "引数1", "引数2"

class構文では必ず親クラスのコンストラクタ処理(super()の呼び出し)を先に行い、その次に子クラスのコンストラクタ処理を行います。

続きについて

この記事はJavaScript Primerを読んで学んだことの第2回目(全3回)になります。
第1回はこちら
第3回についてもいずれ執筆予定です。

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

JavaScript 要素を表示/非表示にする

visibility:hidden;visibility:visible;で表示、非表示を作成する

尚、外部ファイルは取り込まずhtmlファイル内に<script></script>タグを使用して記述

show.html.erb
# クリックして開くところ
<div id="p1" class="dialog">

# クリックすることろ
<svg class="user-circleicon" onclick="clickBtn1()" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>

...

</div>

show.html.erb
<script>
# デフォルトは非表示
 document.getElementById("p1").style.visibility ="hidden";

 function clickBtn1(){
  const p1 = document.getElementById("p1");

  if(p1.style.visibility=="visible"){
   p1.style.visibility ="hidden";
   }else{
   p1.style.visibility ="visible";
   }
 }
</script>

参考:
https://itsakura.com/javascript-display

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

ウェブページを崩壊させてみたいときに唱える呪文

ウェブページの見た目を崩壊させてみたいときに唱える呪文(JavaScript)です。

ウェブページを開いて、開発ツールのコンソールに下記コードをおもむろにペーストして実行。

var lastEl;
var whileLastEl = () => {
  var el = document.lastElementChild;
  while (el !== null) {
    el = el.lastElementChild;
    if (el !== null) {
      lastEl = el;
    }
  }
}
var removeEl = () => {
  el = document.lastElementChild;
  whileLastEl();
  lastEl.remove();
  var timeoutId = setTimeout(removeEl, 100);
    if (lastEl.tagName === 'BODY') {
      clearTimeout(timeoutId);
    }
 }
setTimeout(removeEl, 100);

ウェブページを一番下までスクロールしてみてください。
ひとつずつ要素が消えていきます。

もう少し詳しく説明すると、
DOM ツリーの、末っ子要素の、末っ子要素の、末っ子要素
という具合に null が返されるまで捜査して一番最後の要素を消します。

これを 100ミリ秒ごとに DOM 要素がBODY になるまで繰り返します。

クリックなど、何かイベントに仕込んでもいいかもしれません。

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

Firestore データ取得の制限について

概要

  • 普段大変お世話になっているFirestoreですが、DBから直接データを取得しようとすると、制限があるので自分なりにまとめました。
  • 今回は下記の様な例で説明していきたいと思います。6人の男の子の身長と体重が入ったDBです。

collection名:boys

id name height weight
0 一郎 160 60
1 二郎 165 65
2 三郎 170 70
3 四郎 175 75
4 五郎 180 80
5 六郎 185 85

orderByの制限

不等号を使った複数フィールドの絞り込みができない

  • 例のデータだと、例えば、身長160cm~170cmかつ、体重65kg~80kgの人、みたいな絞り込みが仕様上できません。
  let boysRef = db.collection("boys").where("height", ">=", 160).where("height", "<=", 170).where("weight", ">=", 65).where("weight", "<=", 80); 
  let items = await boysRef.get(); // エラー
  • 出てくるエラーはこんな感じ。不等号を含むwhereフィルターは全て同じフィールドに対しての指定である必要がありますみたいに言われます。
  • 今回の例でいうと、身長160cm~170cmの人、体重65kg~80kgの人、どっちか片方だけの絞り込みはできます。
Uncaught (in promise) FirebaseError: Invalid query. All where filters with an inequality (<, <=, !=, not-in, >, or >=) must be on the same field. But you have inequality filters on 'height' and 'weight'

不等号をつかった絞り込みと、orderByに異なるフィールドを指定できない

  • 今回の例だと、例えば、身長160cm~170cmの人に絞り込みつつ、体重を降順に並び替える、というのができません。
  let boysRef = db.collection("boys").where("height", ">=", 160).where("height", "<=", 170).orderBy("weight", "desc");
  let items = await boysRef.get(); // エラー
  • こんなエラーです。不等号を含むwhereフィルターはheightについてのものなので、最初のorderByのフィルターも、heightについて指定する必要がありますということでした。
Uncaught (in promise) FirebaseError: Invalid query. You have a where filter with an inequality (<, <=, !=, not-in, >, or >=) on field 'height' and so you must also use 'height' as your first argument to Query.orderBy(), but your first orderBy() is on field 'weight' instead.
  • ちなみに、2個目のorderByにweightを指定する↓のやり方だとエラーはでませんが、身長順で並び替えたあと、同順位のデータを体重順で並び替えるだけなので、期待する結果は得られません。
  let boysRef = db.collection("boys").where("height", ">=", 160).where("height", "<=", 170).orderBy("height", "desc").orderBy("weight", "desc");
  let items = await boysRef.get();

ページネーションが実現しづらい

  • まずfirestoreを使ってのページングにはstartAfterを使います。上の例だと以下の通りです。
  let boysRef = db.collection("boys").orderBy("height", "asc").limit(2);
  let items = await boysRef.get(); // itemsには一郎、二郎のデータ
  let last = items[items.length - 1];
  let nextItems = boysRef.startAfter(last.doc).get(); // 三郎、四郎のデータ
  • 上記のやり方だともっと見る形式の追加ローディングにはさくっと対応できますが、googleの検索結果ページにある様なページネーションには対応が困難です。
  • 例えば、100件データがあるうちの51件目から表示したい場合、一発で51件目のデータを特定することが、startAfterでは不可能です。

参考リンク

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

スクロールバーを上下に挿入

業務でスクロールバーを上下に挿入したので、記録に残していきたいと思います。

イメージとしてはこんな感じです。
ev003.JPG
スクロールバー上下が連動して移動できるようになってます。

実装

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

<head>
  <meta charset="UTF-8">
  <title>上下スクロールバー</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div id="containar">
    <div class="scrollbar" id="scrollbar">
      <div class="inner"></div>
    </div>
    <div class="scrollbox" id="scrollbox">
      <div class="inner">
        <table id="data-table">

            <th>項目1</th>
            <th>項目2</th>
            <th>項目3</th>
            <th>項目4</th>
            <th>項目5</th>
            <th>項目6</th>
            <th>項目7</th>
            <th>項目8</th>
            <th>項目9</th>
            <th>項目10</th>
            <th>項目11</th>
            <th>項目12</th>
            <th>項目13</th>
            <th>項目14</th>
            <th>項目15</th>
            <th>項目16</th>
            <th>項目17</th>
            <th>項目18</th>

        </table>
      </div>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
    integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=" crossorigin="anonymous"></script>
  <script src="main.js"></script>
</body>

</html>

tableタグにthをセットします。
※thは「Table Header(見出し)」のことです。

style.css
#containar {
    width: 500px;
    padding: 100px;
  }
  .scrollbar {
    width: 100%;
    overflow-x: scroll;
    overflow-y: hidden;
  }
  .scrollbar .inner {
    width: 2500px;
    height: 1px;
  }
  .scrollbox {
    width: 100%;
    margin-top: 5px;
    overflow-x: scroll;
    overflow-y: hidden;
  }
  .scrollbox .inner {
    width: 2500px;
    margin-bottom: 5px;
    background-color: #F9F8F6;
  }
  .scrollbox .txt {
    margin: 0;
    font-size: 16px;
    line-height: 1.5;
  }

レイアウト変更とスクロールバーをセットします。
この時点では行にデータが入っていなく、上下のスクロールバーが連動していないことがわかります。
では次

main.js
//上下のスクロールバー連動
$(function () {
    //「対象要素.on( イベント名, 関数 )」
    //イベント名がscrollなので、画面がスクロールした時に発動
  $("#scrollbar, #scrollbox").on("scroll", function () {
    if ($(this).attr("id") === "scrollbar") {
        //scrollLeftでスクロール量を取得
      $("#scrollbox").scrollLeft($(this).scrollLeft());
    } else {
      $("#scrollbar").scrollLeft($(this).scrollLeft());
    }
  });
});

//行にデータを挿入
var tableEle = document.getElementById("data-table");

for (var i = 0; i < 5; i++) {
  // テーブルの行を 5行追加する
  var tr = document.createElement("tr");

  for (var j = 0; j < 18; j++) {
    // テーブルの列を 18行追加する
    var td = document.createElement("td");
    td.innerHTML = "データ" + (i + 1) + "-" + (j + 1);
    tr.appendChild(td);
  }
  tableEle.appendChild(tr);
}

このようにすることによって行にデータが入り、上下のスクロールバーが連動していることがわかります

参考

https://qiita.com/kuro-wassan/items/86b6a24787116797a5d0

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

JavaScript クッキーの読み・書き・削除(承認パネル)

クッキーの読み、書き、削除について学んだ。備忘録を残す。

クッキーとは

  • ブラウザ間でやりとりされる小さなデータ。ヴラウザとユーザー間でやりとりされ、主にログイン情報などを管理する。ECサイトやSNSなどで広く使用される。

OSライブラリ「js-cookie」

  • クッキーのデータを操作するのに利用するOpenSourceライブラリ。クッキー操作をするための補助プログラム。 HTMLファイルに読み込ませることでクッキーデータの操作ができるようになる。 ##jsライブラリのメソッド

クッキー保存のメソッド

Cookies.set('変数名','値',{expire: 有効期間});

  • 上記のメソッドを使用すると、指定した変数に有効期間、値が保存される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【技術書まとめ】『JavaScript Promiseの本』を読んだまとめ

Chapter.1 - Promiseとは何か

  • E言語で発見された
    • 並列/並行処理の言語デザインの一種
  • コールバックでエラーを返して、if (error) {}するのとの違い
    • あくまでコーディングルール
      • Promise はthencatchしか使えない
        • 統一される
          • 非同期処理をうまくパターン化する

2. Chapter.2 - Promiseの書き方

  • Promiseのショートカット
    • テストコードに使える
      • Promise.resolve
      • Promise.reject
  • thenableなオブジェクト
    • .thenを持っている
      • Array like みたいなもの
    • jQuery.ajax()の返り値
      • Promise.resolveでpromiseオブジェクトにできる
  • Promise#finally
    • 引数を受け取らない
      • どんな引数を返しても影響なし
    • isLoadingで使う
  • thenは常に新しいpromiseオブジェクトを返す
  • Promise.all
    • promiseをまとめる
      • 全て並列に実行される
  • Promise.race
    • 最初にFulfilledになった時点でthenが呼ばれる
      • 他のpromiseはキャンセルされない

4. Chapter.4 - Advanced

  • throw でなく reject する
    • throw が意図したものか例外なのかわからない

Chapter.5 - Async Function

  • Async Function
    • 必ずPromiseインスタンスを返す
      • Promise.resolve(返り値)を返す
    • awaitが使える
      • 外の処理は止まらない
  • await
    • 右辺のPromiseインスタンスがFullfilledRejectedになるまで待つ
  • Async Functionと配列
    • forを使う
      • 順番が重要でなければPromise.allを使う
    • forEachは使えない
      • awaitAsync Functionの直下でしか使えないから
        • コールバックにするとどうか?
          • 外側の処理が先に進んでしまう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで電卓を作ってみた。

JavaScriptで簡単な電卓を作ってみました。

21/3/4:小数計算に誤りがあったため、修正しました。

主な仕様

  1. 四則演算の電卓。
  2. 連続して計算できるようにしたい。

本当にシンプルな電卓です。

HTML & CSS

Html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UFT-8">
        <title>電卓</title>
        <link rel="stylesheet" href="main.css">
        <script src="jquery-3.5.1.min.js"></script>
    </head>
    <body>
        <p class="show_number">0</p>
        <div class="number_button">
            <table>
                <tr>
                    <td><button class="number" id="Seven">7</button></td>
                    <td><button class="number" id="Eight">8</button></td>
                    <td><button class="number" id="Nine">9</button></td>
                    <td><button class="operator" id="Plus">+</button></td>
                </tr>
                <tr>
                    <td><button class="number" id="Four">4</button></td>
                    <td><button class="number" id="Five">5</button></td>
                    <td><button class="number" id="Six">6</button></td>
                    <td><button class="operator" id="minus">-</button></td>
                </tr>
                <tr>
                    <td><button class="number" id="One">1</button></td>
                    <td><button class="number" id="Two">2</button></td>
                    <td><button class="number" id="Three">3</button></td>
                    <td><button class="operator" id="Asterisk"></button></td>
                </tr>
                <tr>
                    <td><button class="number" id="Zero">0</button></td>
                    <td><button class="number" id="DoubleZero">00</button></td>
                    <td><button class="number" id="Point">.</button></td>
                    <td><button class="operator" id="Slash">/</button></td>
                </tr>
                <tr>
                    <td colspan="2"><button class="Allclear" id="AllClear">AC</button></td>
                    <td colspan="2"><button class="Equal" id="Equal">=</button></td>
                </tr>
            </table>
        </div>
        <script type="text/javascript" src="main.js" charset="UTF-8"></script>
    </body>
</html>

jQueryを使用。
電卓のボタンはtableで配置して整理しやすくしてます。

CSS
.show_number {
    background-color: black;
    color: white;
    width: 300px;
    height: 30px;
    text-align: right;
    font-weight: bold;
    margin-bottom: -1px;
}

.number_button {
    width: 300px;
}

table {
    width: 100%;
}

td {
    width: 25%;
}

button {
    width: 100%;
    font: 16px bold;
}

cssは簡単に、ボタンの体裁を整えるだけ。

JavaScriptで計算

JavaScript
function getDotPosition(float){
    let dotPosition = 0;
    float=String(float);
    if(float.lastIndexOf('.') != -1){
        dotPosition = (float.length-1) - float.lastIndexOf('.');
    }
    return dotPosition;
}

function Calc(num1, num2, ope){
    let result = 0;
    let dotPosition1 = getDotPosition(num1);
    let dotPosition2 = getDotPosition(num2);
    let max = Math.max(dotPosition1, dotPosition2);
    num1 = parseFloat(num1);
    num2 = parseFloat(num2);
    let value1 = parseInt((num1.toFixed(max) + '').replace('.', ''));
    let value2 = parseInt((num2.toFixed(max) + '').replace('.', ''));
    if(ope==3){
        if (max == 1) {
            max = max + 1;
        } else {
            max = max + max;
        }
    }

    let power = Math.pow(10, max);

    if(ope==1){
        result = (value1 + value2) / power;
    }else if(ope==2){
        result = (value1 - value2) / power;
    }else if(ope==3){
        result = (value1 * value2) / power;
    }else if(ope==4){
        result = (value1 / value2);
    }
    return result
}

let num1 = 0;
let num2 = 0; 
let ope = 0;
let point = 0;
let show_text = 0;
let number = 0;
let state = 0;
let negative = 0;

$(document).ready(function(){
    $("button").click(function(){
        let classes = $(this).attr("class");
        let id = $(this).attr("id");
        let text =$(this).text();
        if(id == "AllClear"){
            $(".show_number").text(0);
            num1 = 0;
            num2 = 0;
            ope = 0;
            point = 0;
            show_text=0;
            number=0;
        }else if(id=="minus" && show_text==0){
            negative=1;
        }else if(classes == "number"){
            state=0;
            if(id=="Point"){
                if(point==0){
                    if(show_text==0){
                        number = 0 + text;
                    }else{
                    number = $(".show_number").text() + text;
                    }
                    point = 1;
                    show_text = 1;
                    $(".show_number").text(number);
                }
            }else if(id=="DoubleZero"){
                if(show_text!=0){
                    number = $(".show_number").text() + text;
                    $(".show_number").text(number);
                }
            }else if(id=="Zero"){
                if(show_text!=0){
                    number = $(".show_number").text() + text;
                    $(".show_number").text(number);
                }
            }else{
                if(show_text==0){
                    if(negative==1){
                        text = -1 * text;
                        negative=0;
                    }
                    $(".show_number").text(text);
                    show_text=1;
                }else{
                    number = $(".show_number").text() + text;
                    $(".show_number").text(number);
                }
            }
        }else if(classes=="operator"){
            if(num1==0){
                num1 = $(".show_number").text();
            }else if(num1!=0){
                num2 = $(".show_number").text();
            }
            if(num2!=0 && state != 1){
                result = Calc(num1, num2, ope);
                num1 = result;
                $(".show_number").text(num1);
            }
            if(id=="Plus"){
                ope=1;
                state=1;
            }else if(id=="minus"){
                ope=2;
                state=1;
            }else if(id=="Asterisk"){
                ope=3;
                state=1;
            }else if(id=="Slash"){
                ope=4;
                state=1;
            }
            show_text=0;
            point=0
        }else if(id=="Equal"){
            if(num1!=0){
                num2 =$(".show_number").text();
            }
            result = Calc(num1, num2, ope);
            $(".show_number").text(result);
            show_text = 0;
            point=0;
            ope = 0;
            num1 = 0;
            num2 = 0;
            }
        });
    });
  • 小数点の計算に誤差が出るのでgetDotPosition関数で調整。

  • Calc関数に数字1、数字2、演算を引数にとって計算させる。

    • 計算中に小数点を処理しています。(後述)
  • global変数を用意。

    • num1, num2: 数字2つ分
    • ope: 演算
    • point: 小数点の入力のFlag
    • show_text: 表示が0かどうかのフラグ
    • number: show_textへ反映させる変数
    • state: 演算子Flag
    • negative: マイナス処理のFlag
  • ボタンが押された時に、

    • 押されたボタンのclass, id, 表示を取得。
    • ACが押されたら全てを初期化。
    • 計算の最初にマイナスが押されたら、negativeフラグを立てる。
    • 小数点が押された時、表示の後に続けて小数点を入力し、pointフラグを立てる。
      • pointフラグが立っている時は小数点の入力を受け付けないようにする。
    • 0または00が押された時、電卓の表示が0じゃなければ、その後に入力を足す。
      • 表示が0の時に00を続けて入力できないようにする。
    • それ以外の数字ボタンの時、
      • 表示が0なら押した数字を表示。
        • その時negativeフラグが立っていたら、マイナスの数値で表示。
      • 表示が0じゃなければ、その後に数字を表示。表示フラグを立てる。
    • 演算子の時、
      • num1が登録されてなければ、表示の数字をnum1に登録。
      • num1が登録されていれば、表示の数字をnum2に登録。
      • num2の登録があり、演算子フラグがなければ、
        • Calc関数で計算処理をし、結果をnum1に代入して、電卓に表示。
        • この処理で計算を続けて行うことができるようになる。
      • 各演算子に割り当てた変数をopeに代入して、演算子フラグを立てる。
        • 演算子フラグにより、演算子を間違って押しても、計算処理をせずに押しなおすことができる。
      • 表示フラグを0に、小数点フラグを0に戻す。
    • =ボタンの時、
      • num1が登録されていれば、num2に表示を代入する。
      • Calc関数で計算をする。
      • 電卓上に結果を表示。
      • 各フラグを戻す。

小数点の計算について

小数点の計算は誤差が出ることがあるので、その処理をする必要があります。
ライブラリを導入する方法もあるようですが、今回はライブラリは使用してません。
数値を文字列に直して、小数点の位置をlastIndexOf関数で確認していきます。(String, getDotPosition)
小数点の位置が分かったら、小数点を解消するために桁数を記録しておきます。(Calc関数dotPosition1, dotPosition2, max)
数値に直した後 (parseFloat)、小数点を解消します(replace)。
桁数を別に置いておいて (power)、計算後に小数点の処理をします。

この処理をしておくと、誤差がなくなり正しく計算できます。

完成

スクリーンショット 2021-03-03 8.33.19.png

 参考資料

jQueryでクリックされたid値をとる
JavaScript:計算誤差の対処
JavaScriptでの小数点の計算の誤差について

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

JavaScriptでストップウォッチを作ってみた。

HMTL CSS JavaScriptで簡単なストップウォッチを作ってみた。

主な仕様

  1. 0.1秒単位でカウントアップ

  2. 一時停止機能あり

  3. リセットボタンでカウントを初期化

本当に簡単なものになっています。

HTML & CSS

まずはHTMLとCSSでストップウォッチの形を作ります。

HTML
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>ストップウォッチ</title>
        <link rel="stylesheet" href="main.css">
        <script src="jquery-3.5.1.min.js"></script>
    </head>
    <body>
        <h3>ストップウォッチ</h3>
        <div class="time_count">
            <p id="hour">0</p>
            <p>:</p>
            <p id="minute">0</p>
            <p>:</p>
            <p id="second">0</p>
            <p>:</p>
            <p id="m_second">0</p>
        </div>
        <button class="button" id="start" onclick="count_start()">スタート</button>
        <button class="button" id="stop">ストップ</button>
        <button class="button" id="reset">リセット</button>
        <script type="text/javascript" src="main.js" charset="UTF-8"></script>
    </body>
</html>

今回はjQueryを使います。
time_countクラスでタイマー部分を作成し。buttonで各種ボタンを配置します。

CSS
.time_count {
    display: flex;
}

.time_count p {
    margin-left: 10px;
    margin-right: 10px;
    font-size: 25px;
    font-weight: bold;
}

.button {
    width: 100px;
    height: 50px;
    border: 1px solid;
    font-weight: bolder;
}

CSSは簡潔に、ボタンの大きさやフォントの大きさを設定。
time_countクラスをflexにして、横並びにタイマーらしく見せています。

JavaScriptで動きをつける

JavaScript
function TimeCount(){
    let ms = $("#m_second").text();
    let sec = $("#second").text();
    let min = $("#minute").text();
    let hour = $("#hour").text();   
    ms++;
    $("#m_second").text(ms);
    if ($("#m_second").text()==10){
        sec++;
        $("#m_second").text(0);
        $("#second").text(sec);
    }
    if ($("#second").text()==60){
        min++;
        $("#second").text(0);
        $("#minute").text(min);
    }
    if ($("#second").text()==60){
        min++;
        $("#second").text(0);
        $("#minute").text(min);
    }
    if ($("#minute").text()==60){
        hour++;
        $("#minute").text(0);
        $("#hour").text(hour);
    }
}

let start = 0;
let time_count = null;

$(document).ready(function(){
    $("#start").click(function(){
        start += 1
        if (start==1){ 
            time_count = setInterval(TimeCount, 100);
        };
    });
    $("#stop").click(function(){
        clearInterval(time_count);
        start = 0;
    });
    $("#reset").click(function(){
        clearInterval(time_count);
        start=0;
        $("#hour").text(0);
        $("#minute").text(0);
        $("#second").text(0);
        $("#m_second").text(0);
    });
});
  • TimeCount関数でタイマーの動きを制御します。

    • msが1つずつ増加していく。
    • msが10になったらs (秒)が1増加。
    • sが60になったらm (分)が1増加。
    • mが60になったらh (時)が1増加。
    • それぞれ、位が上がったら0に戻す(msが10になった時、sを+1してmsは0にする)。
    • 関数が呼び出された時は、タイマーの続きから始められるように初期値を代入する。
  • start変数を置いて、startボタンが押されたら1ずつ追加。

    • start変数が1の時(startボタンが1回押された時)にタイマースタート。 
    • この変数がないと、連続してstartボタンが押せてしまう。
  • setInterval関数で100msずつカウントする。

  • time_count変数にsetIntervalを代入してclearできるようにする。

    • time_count変数をどの関数からでも操作できるように、Global変数(関数の外におく)にする。
  • ストップボタンで一時停止する

    • clearIntervalだけで初期化しないため、すぐにstartを押すと、一時停止したところから再開可能。
  • リセットボタンでタイマーを止めた後、全てを初期化する

  • ストップボタンやリセットボタンが押された時は、start変数を0にして、再度スタートできるようにしておく。

完成

スクリーンショット 2021-03-03 7.04.41.png
スクリーンショット 2021-03-03 7.04.25.png

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

フレームワークを使わないPHP+Javascript TIPS

サイトマップを作る

記載するほどでもないが、サイトマップを作るコード。
最終的にxmlファイルを生成するが、phpファイルのままでもいけるかもしれない。

make_stemap.php
$xml = '<?xml version="1.0" encoding="utf-8" ?>'."\n";
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
foreach($array_movie_id as $value){
    $xml .= "<url>";
    $xml .= "<loc>https://~/".$value['key']."</loc>";
    $get_date = $value['key2'];
    $mod_date = substr($get_date, 0, -9);
    $xml .= "<lastmod>".$mod_date."</lastmod>";
    $xml .= "</url>";
}
$xml .= "</urlset>";
<urlset>
file_put_contents($_SERVER['DOCUMENT_ROOT'].'/xml/sitemap.xml', $xml);

ページを複製する

通常1つのページを作成し、PPOSTで渡した値で表示を変えるのが通例だが、
SEOの観点から複数のページを作成したいときがある。

元ファイルがJavascriptの場合

定型部分をJavascriptで記載し、incファイル化
ob_start()関数を使用。元ファイルがJavascriptなのでPHPの変数の影響を受けることなく記載可能。

a.inc
コード内部はJavascriptで記載。
b.php
データベースに接続等
ob_start(); //バッファ制御スタート
include("inc/a.inc");
$text = ob_get_clean(); //バッファ制御終了&変数を取得
$something = 可変部分
$something .= $text;

元ファイルがphpの場合

複製元でデータベースに接続していたり、include関数を使っていたりすると複製するためのPHPの段階では上手く回っても、複製先ではphpファイルを読み込まない。

foreachの中でディレクトリも作成し、その中にコピーを作成。

a.php
$path = '../directory';
foreach($array as $value){
    if(in_array($value['key'], $file_name_all)){

    }else{
        mkdir($path."/".$value['key']);
        copy('b.php', $path."/".$value['key']."/index.php");
    }
}
?>

$value[key]というディレクトリーの中にindex.phpというファイルが生成される。
indexと名前がつけばURLにディレクトリを入れるだけでそのページを表示するので、楽。

ajaxによる非同期通信(jQueryを用いた場合)

毎回迷うので、備忘録もかねて。

a.php
//データベースに書き込むためのphp
$.ajax({
   url: 'b.php',
   type: 'post', 
   dataType: 'json', 
   data: { // 送信データを指定。上記のURLに$_POST[~]として送信。
         name: $('#name').val(),//jQueryの文法。id="name"のvalueを取得。
         id: $('#id').val(),
         }
     })

読み込みについては後日記載。

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

TypeScriptの型一覧と使用方法まとめ

この記事について

この記事はThe TypeScript HandbookのBasic typesを参考に作成したものになります。
TypeScriptの基本的な型の一覧と使い方をまとめてみました。

基本の型

Boolean

最も基本的なデータ型は単純なTrue/False値で、TypeScriptではboolean値と呼ばれています。

let isDone: boolean = false;

Number

JavaScript と同様に、TypeScript のすべての数値は浮動小数点値か BigIntegers です。これらの浮動小数点値はnumberの型を取得し、BigIntegersはbigintの型を取得します。16進数と10進数のリテラルに加えて、TypeScriptはECMAScript 2015で導入された2進数と8進数のリテラルもサポートしています。

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;

String

JavaScriptでウェブページやサーバー用のプログラムを作成する際のもう一つの基本的な部分は、テキストデータを扱うことです。他の言語と同様に、これらのテキストデータ型を参照するために文字列型を使用します。JavaScriptと同様に、TypeScriptでも二重引用符(")や一重引用符(')を使って文字列データを囲んでいます。

let color: string = "blue";
color = 'red';

また、テンプレート文字列を使用することもできます。これらの文字列はbacktick/backquote (`)で囲まれており、埋め込み式は${ expr }の形をしています。

let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.

I'll be ${age + 1} years old next month.`;

これは、このように文章を宣言していることに相当します。

let sentence: string =
  "Hello, my name is " +
  fullName +
  ".\n\n" +
  "I'll be " +
  (age + 1) +
  " years old next month.";

Array

TypeScript は JavaScript と同様に、値の配列を扱うことができます。配列の型は2つの方法のうちの1つで書くことができます。1つ目の方法では、要素の型の後に[]をつけて、その要素の型の配列を表します。

let list: number[] = [1, 2, 3];

2つ目の方法では、Arrayという汎用的な配列型を使用します。

let list: Array<number> = [1, 2, 3];

Tuple

タプル型を使用すると、型は既知ですが、同じである必要はありません。例えば、ある値を文字列と数値のペアで表現したい場合などです。

// タプル型の宣言
let x: [string, number];
// 初期化
x = ["hello", 10]; // OK
// 誤った初期化
x = [10, "hello"]; // Error
// Type 'number' is not assignable to type 'string'.
// Type 'string' is not assignable to type 'number'.

既知のインデックスを持つ要素にアクセスすると、正しい型が取得されます。

// OK
console.log(x[0].substring(1));

console.log(x[1].substring(1));
// Property 'substring' does not exist on type 'number'.

既知のインデックスのセット外の要素へのアクセスはエラーで失敗します。

x[3] = "world";
// Tuple type '[string, number]' of length '2' has no element at index '3'.

console.log(x[5].toString());
// Object is possibly 'undefined'.
// Tuple type '[string, number]' of length '2' has no element at index '5'.

Enum

JavaScript の標準的なデータ型のセットに加えて便利なのが enum です。C# のような言語では、enum は数値のセットにより親しみやすい名前を付ける方法です。

enum Color {
  Red,
  Green,
  Blue,
}
let c: Color = Color.Green;

デフォルトでは、列挙型のメンバの番号は 0 から始まります。 これを変更するには、メンバの値を手動で設定します。例えば、先ほどの例では0ではなく1から始めることができます。

enum Color {
  Red = 1,
  Green,
  Blue,
}
let c: Color = Color.Green;

あるいは、列挙されているすべての値を手動で設定することもできます。

enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}
let c: Color = Color.Green;

列挙型の便利な機能は、数値から列挙型の中の値の名前に移動することができることです。例えば、値2を持っていたが、上のカラー列挙型の中で何にマッピングされているのかわからなかった場合、対応する名前を調べることができます。

enum Color {
  Red = 1,
  Green,
  Blue,
}
let colorName: string = Color[2];

// 'Green'を表示します。
console.log(colorName); //Green

Unknown

アプリケーションを書いているときにはわからない変数の種類を記述する必要があるかもしれません。これらの値は動的なコンテンツ(例えばユーザからのもの)から来るかもしれませんし、API内の全ての値を意図的に受け入れたいかもしれません。このような場合には、コンパイラや将来の読者に、この変数は何でもあり得ることを伝える型を提供したいので、未知の型を与えます。

let notSure: unknown = 4;
notSure = "maybe a string instead";

// OK, definitely a boolean
notSure = false;

型が不明な変数がある場合、typeofチェック、比較チェック、または後の章で説明するより高度な型ガードを行うことで、より具体的なものに絞り込むことができます。

declare const maybe: unknown;
// 'maybe' は文字列、オブジェクト、ブーリアン、未定義、その他の型になります。
const aNumber: number = maybe;
// Type 'unknown' is not assignable to type 'number'.

if (maybe === true) {
  // TypeScript は maybe がブール値であることを知っています。
  const aBoolean: boolean = maybe;
  // ですから、文字列にはなりません。
  const aString: string = maybe;
// Type 'boolean' is not assignable to type 'string'.
}

if (typeof maybe === "string") {
  // TypeScript は maybe が文字列であることを知っています。
  const aString: string = maybe;
  // ということは、booleanであることはできません。
  const aBoolean: boolean = maybe;
// Type 'string' is not assignable to type 'boolean'.
}

Any

状況によっては、すべての型情報が利用できるわけではなかったり、その宣言に不適切な努力が必要だったりすることがあります。このような状況は、TypeScriptやサードパーティのライブラリを使わずに書かれたコードの値に対して発生することがあります。このような場合には、型チェックをオプトアウトしたいと思うかもしれません。そのためには、これらの値に any 型のラベルを付けます。

declare function getValue(key: string): any;
// OK、'getValue' の戻り値がチェックされていない
const str: string = getValue("myString");

any型は、既存のJavaScriptを操作するための強力な方法であり、コンパイル中に型チェックを徐々にオプトインしたりオプトアウトしたりすることができます。

unknown とは異なり、any 型の変数では、存在しないプロパティであっても任意のプロパティにアクセスすることができます。これらのプロパティには関数が含まれ、TypeScript はそれらの存在や型をチェックしません。

let looselyTyped: any = 4;
// OK, ifItExists が実行時に存在する可能性があります。
looselyTyped.ifItExists();
// OK, toFixedが存在する(ただしコンパイラはチェックしない)
looselyTyped.toFixed();

let strictlyTyped: unknown = 4;
strictlyTyped.toFixed();
// Object is of type 'unknown'.

anyはあなたのオブジェクトを伝搬し続けます。

let looselyTyped: any = {};
let d = looselyTyped.a.b.c.d;
//  ^ = let d: any

結局のところ、便利なものはすべて型の安全性を失う代償を伴うことを覚えておいてください。型の安全性は TypeScript を使う主な動機の一つであり、必要のないときには any を使わないようにするべきです。

Void

void は any の反対のようなもので、型を全く持たないことを意味します。値を返さない関数の戻り値の型としてよく見かけるかもしれません。

function warnUser(): void {
  console.log("This is my warning message");
}

void 型の変数を宣言しても、null (--strictNullChecks が指定されていない場合のみ、次のセクションを参照してください) か未定義しか代入できないので、あまり意味がありません。

let unusable: void = undefined;
// `--strictNullChecks` が与えられなければOK
unusable = null;

NullとUndefined

TypeScriptでは、undefinedとnullはそれぞれundefinedとnullという名前の型を持っています。void と同様に、これらの型は単体ではあまり便利ではありません。

// これらの変数に代入できるものはあまりありません!
let u: undefined = undefined;
let n: null = null;

デフォルトでは、null と undefined は他のすべての型のサブタイプです。つまり、number のようなものに null と undefined を代入することができます。

しかし、--strictNullChecks フラグを使用すると、null と undefined は unknown、any、およびそれぞれの型にのみ代入可能です (ただし、undefined は void にも代入可能です)。これにより、多くの一般的なエラーを回避することができます。文字列、または null または undefined のいずれかを渡したい場合は、string | null | undefined という組合わせ型を使用することができます。

Union型については、後の章で詳しく説明します。

Never

never型は、決して発生しない値の型を表します。例えば、関数式や矢印関数式で常に例外を投げるものや、絶対に返さないものは never が返り値の型となります。また、変数は、決して真であることができない任意の型ガードで絞られたときに never 型を取得します。

never型はすべての型のサブタイプであり、代入可能ですが、どの型もneverのサブタイプではなく、代入可能です(never自身を除く)。anyでさえ、 neverに代入可能ではありません。

neverを返す関数の例をいくつか挙げます。

// 関数の戻り値は到達可能な終点を持っていてはいけない
function error(message: string): never {
  throw new Error(message);
}

// 推定された戻り値の型は決してありません。
function fail() {
  return error("Something failed");
}

// 関数の戻り値は到達可能な終点を持っていてはいけない
function infiniteLoop(): never {
  while (true) {}
}

Object

objectは非原始型、つまりnumber、文字string、boolean、bigint、記号、null、未定義でないものを表す型です。

オブジェクト型を使うと、Object.createのようなAPIでより良い表現ができるようになります。例えば

declare function create(o: object | null): void;

// OK
create({ prop: 0 });
create(null);
create(undefined); // undefined は null のサブタイプではないことに注意してください。
// Argument of type 'undefined' is not assignable to parameter of type 'object | null'.

create(42);
// Argument of type '42' is not assignable to parameter of type 'object | null'.
create("string");
// Argument of type '"string"' is not assignable to parameter of type 'object | null'.
create(false);
// Argument of type 'false' is not assignable to parameter of type 'object | null'.

一般的には、これを使う必要はありません。

型のアサーション

ある値について、TypeScript が行うよりも多くのことを知っているという状況に陥ることがあります。通常、これはあるエンティティの型が現在の型よりも特定の型である可能性があることを知っている場合に起こります。

型アサーションは、コンパイラに「信じてくれ、自分が何をしているのか分かっている」と伝えるためのものです。型アサーションは他の言語の型キャストのようなものですが、特別なチェックやデータの再構築は行いません。実行時の影響はなく、コンパイラによって純粋に使用されます。TypeScript は、プログラマであるあなたが必要な特別なチェックを行ったと仮定しています。

型アサーションには2つの形式があります。

一つは as構文 です。

let someValue: unknown = "this is a string";

let strLength: number = (someValue as string).length;

もう一つのバージョンはangle-bracket構文です。

let someValue: unknown = "this is a string";

let strLength: number = (<string>someValue).length;

2つのサンプルは同等です。どちらか一方を使うかは好みの問題ですが、JSX で TypeScript を使う場合は、as-style アサーションのみが許可されています。

letに関する注意事項

これまでのところ、JavaScriptのvarキーワードの代わりにletキーワードを使ってきたことにお気づきかもしれません。let キーワードは、実は TypeScript が利用できるようにした新しい JavaScript の構文なのです。let と const が var の多くの問題を解決する方法については、変数宣言に関するハンドブックリファレンスを参照してください。

Number, String, Boolean, Symbol, Objectについて

Number, String, Boolean, Symbol, Object の型は、上記で推奨されている小文字のバージョンと同じだと考えたくなるかもしれません。しかし、これらの型は言語プリミティブを参照するものではなく、ほとんどの場合、型として使用すべきではありません。

function reverse(s: String): String {
  return s.split("").reverse().join("");
}

reverse("hello world");

代わりに、number、string、boolean、object、symbolの型を使用します。

function reverse(s: string): string {
  return s.split("").reverse().join("");
}

reverse("hello world");

まとめ

以上、TypeScriptの基本的な型の一覧とそれらの使い方でした!
TypeScriptを学びたい方にとって、お役に立てれば幸いです!

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

Amazonのポイントを利用しないで貯めるためのUserScript

姉さん、Amazon Kindle本の50%ポイント還元セールです。

【50%ポイント還元】Kindle本キャンペーン
期間:2021年2月19日(金)~2021年3月4日(木)(日本時間)

せっかくの機会なので、ポイントを貯めに貯めよう!

利用環境

UserScript

userscript.js
// ==UserScript==
// @name         do not use amazon point.
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://www.amazon.co.jp/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...
    document.querySelectorAll('input#balance-checkbox-0').forEach(function(elm) {
        if(elm.checked) {
            elm.click();
        }
    });
})();

ラムダ式のありがたみを噛みしめるため、敢えて使わない派。

効果

『ポイントを利用する』のチェックが外れてうっかり利用が減って、ポイントをためやすい。
image.png
『みたむらくん』最高です。

備考

Amazonのデジタル コンテンツの場合、デフォルトが『ポイントを利用する』だったので、こんなUserScriptを作って久しいのだけど。

もしかして、いつのまにかデフォルトでOFFに出来る様になってたりする?:thinking:

ちなみに私はポイント貯めるマンなので、うん万分貯めてスカッと使いたい派です。(PS3/PS4はヨドバシのポイントだけで買いました。) そんな同士用のスクリプト。

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

TypeScriptをなんとなく理解するための記事④ ~構造型~

この記事は何?

TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第4弾です!

1~3弾は、TypeScriptの書き方にフォーカスしましたが、
この記事では、TypeScriptの特徴であるStructural Type System(構造型システム)について説明していきます。

過去の記事も合わせて御覧ください!

  1. JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
  2. TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~
  3. TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~
  4. TypeScriptをなんとなく理解するための記事④ ~構造型~ ← この記事

TypeScriptがなんとなく理解できたら、是非インストールして、触ってみてください!
→ 【TypeScriptを導入しよう!】エンジニアもすなるTypeScriptといふものを、我もしてみむとてするなり(3/3 作成予定)

構造型って何だ?

よく比較されるものとして、公称型と構造型があります。
公称型に関しては、JavaやPHPの型システムに利用されており、クラス名の一致で型の互換性(同じような型かどうか)を識別します。

一方で、TypeScriptの構造型は何なのでしょうか?
構造型(Structural Type)は、型の構造さえ同じであれば互換性があると判断する仕組みのことです。

この記事では、TypeScriptの大きな特徴である、構造型について理解していきます。

コードから構造型の理解を深める

では、実際にコードを見ながら構造型について理解していきましょう。
以下に、型の構造が同じコードを書いてみました。

// { name: string, cry: string }の構造を持つAnimal型を作成
interface Animal {
  name: string;
  cry: string;
}

const animalCryInfo = (animal: Animal) => {
  console.log(`${animal.name}の鳴き声は、${animal.cry}`);
}

// vividMorayは{ name: string, cry: string }と同じ構造 → Animal型と同じ
const vividMoray = { name: 'ハナヒゲウツボ', cry: 'ありません' };
// vividMorayは、引数がAnimal型と構造が一致するので、動作する!
animalCryInfo(vividMoray); // ハナヒゲウツボの鳴き声は、ありません

最初にinterfaceを利用して、{ name: string, cry: string }の構造を持つAnimal型を作成しています。

interface Animal {
  name: string;
  cry: string;
}

次に、Animal型を引数に持つ関数animalCryInfoを定義しています。

const animalCryInfo = (animal: Animal) => {
  console.log(`${animal.name}の鳴き声は、${animal.cry}`);
}

次に、オブジェクトvividMorayを定義します。このオブジェクトの型は推論により{ name: string, cry: string }と解釈されます。

const vividMoray = { name: 'ハナヒゲウツボ', cry: 'ありません' };

最後にanimalCryInfo関数の引数に、オブジェクトvividMorayを入れてみます...。
もし、Animal型とオブジェクトvividMorayが同じ型(構造)であれば、問題なく実行されますが。。。

animalCryInfo(vividMoray); // ハナヒゲウツボの鳴き声は、ありません

問題なくハナヒゲウツボの鳴き声は、ありませんと出力されます!

このように、型の構造に焦点を当てて、型チェックする仕組みが構造型システム(Structural Type System)です。

TypeScriptを記述するときは、この構造型を意識して書いたほうが良さそうです!
さもなくば以下のようなエラーが出てしまいます。?

interface Animal {
  name: string;
  cry: string;
}

const animalCryInfo = (animal: Animal) => {
  console.log(`${animal.name}の鳴き声は、${animal.cry}`);
}


const vividMoray = { name: 'ハナヒゲウツボ'};
animalCryInfo(vividMoray);
// タイプの引数 '{ name: string; } 'はタイプ'Animal'のパラメータに割り当てることはできません。
// プロパティ'cry'がタイプ '{ name: string;} 'にありませんが、タイプ' Animal'.ts(2345)では必須です

終わりに

今回は第1~3回と異なったTypeScriptの特徴を紹介しました。
この記事を見て、TypeScriptを触ってみよう!という人が増えていただければ幸いです?

わかりやすかったら、是非LGTM✨お願いします!(励みになります!)

参考文献

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

【JavaScript】クロネコヤマトの404ページでダンボールをこじ開けてみた

先日バズっていた以下のツイート↓

どうやらクロネコヤマトのウェブサイトの404ページが面白いらしい

image.png

こんなことになっている。
そう、ダンボールを開けまくってクロネコを探すミニゲーム(制限時間30秒)が始まるのである。

30秒?俺には余裕だぜ...3秒で見つけ出してやる。

こいや!!!!

すみませんでした

kuroneko1.gif

い、いない...?!クソッ、どこに...
グッ...指がもげる...

kuroneko2.gif

(ラスト4箱目で発見)

image.png

赤文字に完全に煽られてしまった。
覚えてろよ!!!!

フロントエンジニアをなめるな

ここまで苔にされるのは久しぶりだ。このままじゃ引き下がれない。

要は箱を全部開ければいいんだろ?
やってやるさ。

ソースコード偵察

image.png

image.png

ふーん?

image.png

(↓/assets/js/notfound.jsから抜粋)
image.png

ほーん?(めっちゃコメントついてて読みやすいやんけ...)
わかった。つまり、これをこうして...

for(var i=1; i<=30; i++) {
    const className = `.box-game-item-${('00'+i).slice(-2)}>a`;
    $(className).eq(0).removeClass("disabled");
    $(className).eq(0).click();
}

こうじゃ

image.png

復讐の時

歯ァ食いしばれ!!!

kuroneko3.gif
オラァ!!!

image.png

image.png

おわり

(クロネコヤマトさんへ:純粋に楽しいゲームでした。ありがとうございました)

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

超初心者向けざっくり学ぶWebフロントエンド入門 ~前編~

はじめに~これを書いた目的~

大学の後輩でwebプログラミングについて興味のある方が多かったので身内で簡単なwebの勉強会を開いたが、そのまとめ・補足用の資料を作りたかったため。

対象読者

プログラミング・Webデザイン初心者。Web制作の大枠を知りたい方向け。
それぞれの内容について深堀りするような記事ではないので、詳しく知りたい方は参考にならないかも。

後、HTML,CSSがほとんどなのでwebデザインの内容が比重高いです。
また、極力専門的な用語を省いて書くようにしています。

環境構築

HTML,CSS,JSは環境構築をする必要がありません。ブラウザとテキストエディタさえあれば十分なので、お好きなテキストエディタとブラウザを使用してください。ブラウザはGoogleChrome推奨です。テキストエディタに関して、私はVisualStudioCodeを使っています。

vscodeのダウンロードページへのリンク
https://azure.microsoft.com/ja-jp/products/visual-studio-code/

HTML・CSSとは?

HTMLはHyperText Markup Languageの略。すごいざっくり説明すると、webページの骨組みを作る言語。厳密にはプログラミング言語ではない。

CSSはCascading Style Sheetsの略。こちらは、HTMLで作った骨組みに装飾をするための言語。こちらも同じくプログラミング言語ではない。

JS(JavaScript)とは?

プログラミング言語の1つ。ブラウザ上で動かすことができる。webサイトのページをプログラム上から書き換える、イベント処理(クリックしたら〇〇を表示)、webサイトに複雑なアニメーションをつけるなどが可能。

HTML・CSS入門

文字だけのシンプルなサイト

文字だけが表示されているシンプルなwebサイト。ブラウザでファイルを開くと、「hello」とだけ表示される。
HTMLは<p>などのタグと呼ばれるもので文を囲んでいくことでwebページの骨組みを作る。
見出しや本文などwebページの内容は基本的に<body></body>の間に書く。ちなみに、pタグ(<p></p>)はparagraph(段落)の略。

<!DOCTYPE html>
<html lang="ja"> 
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>文字だけです</title>
</head>
<body>
  <p>hello</p>

</body>
</html>

装飾したサイト(シンプル)

HTMLに装飾をしたい場合、CSSを書く必要があると言ったが、CSSはHTMLファイルに直接書かず、別途ファイルを作りそれを読み込んでHTMLにCSSを適用するケースが多い。

sample1_00.html
<!DOCTYPE html>
<html lang="ja"> 
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cssを読み込んでwebサイトを装飾する</title>
  <link rel="stylesheet" href="./css/style.css">
</head>
<body>
  <p>hello</p> 
</body>
</html>

style.css
/*pタグの文字色を青色に変えて、フォントサイズを20pxにする*/

p {
  color: blue;
  font-size: 20px;
}

<link rel="stylesheet" href="./css/style.css">は、ルートディレクトリ(1番上の階層のこと)のcssというフォルダのstyle.cssというスタイルシートを読み込むよという意味。タグの種類には、一番大きな見出しを表すh1タグ、要素をグループ化するdivタグなど他にも多数あるので必要に応じて是非自分で調べてみて欲しい。「HTML タグ 種類」などで調べてると出てくるかも。

cssでは基本的に、
cssを適用させたい場所{
どういう装飾をするか:色やサイズなどの細かいプロパティ;
}

という書き方をする。今回は、pタグに対して色を青に変えて、フォントサイズを20pxに変えたかったので上記のようになった。

別ページのリンクを貼るには

aタグにリンク先のURLを指定してあげることでwebサイトのリンクを貼ることができる。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
<a href="https://qiita.com/helloworld193/items/9aed3870be1e739c3ad2">過去に書いた別の記事。みてくれると嬉しいです。</a>
</body>
</html>

画像を表示させたい場合

画像を表示したい場合、imgタグを使う。cssファイル同様、表示させたい画像のパス(ファイルの置き場所)を書く。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
<img src="./img/sample.jpg" alt="">
</body>
</html>

image.png

cssを適用させる場所を細かく指定したい場合

前述の内容でcssの読み込み方や装飾方法を学んだ。しかし、このままだとある問題が発生する。

pタグやh1タグを直接指定した場合、ファイル内の全てのpタグもしくはh1タグにスタイルが適用されてしまうという問題だ。

じゃあ、どうするか。

独自に名前を付けて、スタイルを適用する場所でその名前を指定してあげればいい。そうすれば、〇〇という名前がついたpタグ、h1タグというように細かく指定できる。それを可能にするのが、id属性class属性だ。

class属性

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>class属性</title>
  <link rel="stylesheet" href="./css/style.css">
</head>
<body>
  <p class="give_class">class属性がついています</p>
</body>
</html>
style.css
.give_class{
  color: rgb(29, 117, 88);
  font-size: 20px;
}

image.png

id属性

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
    <p id="give_id">id属性が付与されてます</p>
</body>
</html>
style.css
#give_id{
  font-size: 40px;
  color: rgb(255, 189, 109);
}

image.png

簡単にまとめると、idをcssで指定する場合は#id名、classを指定する場合は、.class名で指定してあげる。

idとclassの違い

ここで勘の鋭い方もそうではない方も1つ疑問に思ったかもしれない。
「名前をつけるためにidとclassの2つあるのはなぜ? もし、名前をつけるためならどちらか1つで十分なはず。にもかかわらず2つあるのには何か理由があるのでは?」 と。

実は、idとclassにはいくつか違いがあり、以下のようになっている。

  1. idは同じwebページ内で何度も同じ名前(id名)を使うことができないのに対し、classは同じ名前(class名)を複数使用することができる。学生っぽく例えると、classは学科名でidは学籍番号みたいなイメージ。同じ学科の人はたくさんいるが、同じ学籍番号の人は存在しないはず。

  2. cssが適用される優先順位はidの方が上

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
    <p id="give_id" class="give_class">id属性につけたスタイルが優先されます</p>
</body>
</html>
style.css
#give_id {
    font-size: 40px;
    color: rgb(144, 202, 120); /* 緑 */
}

.give_class {
    font-size: 40px;
    color: rgb(123, 41, 255);  /* 赤 */
} 
  1. idを使うとページ内リンク(同ページの特定箇所に移動できる)を貼ることができる
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
    <p id="give_id">ここですここここここここ</p>
    <br>
    <p>テキスト</p>
    <p>テキスト</p>
    <br>
    <p>テキスト</p>
    <p>テキスト</p>
    <br>
    <p>テキスト</p>
    <p>テキスト</p>
    <br>
    <p>テキスト</p>
    <p>テキスト</p>
    <br>
    <p>テキスト</p>
    <p>テキスト</p>
    <br>
    <p>テキスト</p>
    <p>テキスト</p>
    <p><a href="#give_id">ここをクリックすると、give_idのidがついた箇所に移動する</a></p>

</body>
</html>
style.css
#give_id {
    font-size: 20px;
    color: rgb(144, 202, 120);
}

.give_class {
    font-size: 20px;
    color: rgb(123, 41, 255);
}

hqm7d-gfmye.gif

余白を設定したい場合

cssで余白を設定したい場合、paddingもしくはmarginを使って余白の指定をする。

簡単に説明すると、paddingは境界線の内側の余白、marginは外側の余白だ。

image.png

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
<p class="content">要素</p>
<p class="content">要素</p>
<p class="content">要素</p>
</body>
</html>
style.css
.content{
    padding: 20px;
    margin: 10px;
    background-color: rgb(255, 194, 115);
    border: 2px solid#696969;
}

image.png

この場合、要素の境界線の外側に10px、内側に20px余白を入れている。

JS入門

HelloWorld!してみる

プログラミング初心者が必ず通る道と言われてるHelloWorldプログラムを作ってみる。
HelloWorldを知らない人がほとんどだと信じて軽く説明しておくと、「HelloWorld」という文字を表示するだけ。
余談だが、僕のアカウント名の由来もこれだったりする。当時、名前から全力で初心者感を出そうと思ってたらこれに行き着いたっていう。

JSを書く方法は主に2通り。

  • HTML内のscriptタグの中に直接書く方法
  • 別ファイルで分けてそれをscriptタグを使って読み込む方法

HTML内のscriptタグの中に直接書く場合

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        console.log("HelloWorld")
    </script>
</body>

</html>

ブラウザで確認してみると、何も表示されていない。

これは上手く言ってないのではと思うかもしれないが、実はJSを実行することができている。

まず、ブラウザにはデベロッパーツールと言うものがあり、それを使ってページの動作のチェックなどができるのだが、キーボードのF12を押すと出てくるので確認してみよう。

すると、その中にConsole(画像の赤線で囲まれた箇所)という名前の箇所があると思うので、それをクリック
image.png

すると、Consoleの部分にHelloWorldと表示されているのがわかる。
image.png

ちなみに、気になった人もいるかもしれないので補足しておくと、scriptタグをbodyタグの最後に書くとwebページの表示速度が速くなるので、scriptタグはbodyタグの最後に書くことを推奨する。

別ファイルで分けてそれを読み込む場合

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>別ファイルで分けてそれを読み込む場合</title>
</head>
<body>
    <script src="./HelloWorld.js"></script>
</body>
</html>
HelloWorld.js
console.log("HelloWorld");

変数

変数とは、ざっくり言うと値につけるラベルのようなもの。

余談だが、変数は箱で例える派とラベルorタグで例える派がいるので、人によって若干説明は変わるかもしれない。でも、書いてたら自然とイメージできてくるのでとりあえずアウトプットすることを意識してほしい。

変数に数値を代入するとき

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

</body>
    <script>
        let password = 1234;  //変数の定義
        console.log(password);
    </script>
</html>

実行結果

image.png

このプログラムの場合、passwordという名の変数を定義していて、それに1234という数値を入れてConsoleに表示している。

では、数値ではなく文字になった場合はどうなるだろうか。

変数に文字(列)を代入するとき

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

</body>
    <script>
        let username = "SatoTaro";
        console.log(username);
    </script>
</html>

image.png

上記のプログラムでは、SatoTaroという文字列をConsoleに表示している。
変数に文字列を代入する場合は、代入するときに""で囲んで使う。ちなみに、数値を""で囲んだ場合も文字列扱いになる。

文字列の連結

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

</body>
    <script>
        let username = "SatoTaro";
        console.log("あなたのユーザー名は"+username+"です");
    </script>
</html>

image.png

文字列を連結させたい場合は、+記号を使って連結させる。

計算

演算子と言われるものを知っていれば、電卓みたいに計算できる。(Excel触ったことある人は理解しやすいかも)

indnex.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>計算</title>
</head>
<body>
    <script>
        console.log(1 + 1); //和
        console.log(5 - 3); //差
        console.log(2 * 3); //積
        console.log(6 / 3); //商
        console.log(7 % 3); //余剰
    </script>
</body>
</html>

image.png

条件分岐(if, else文)

これまでConsoleに文字を表示する方法、変数を使う方法を学んできた。でも、これだと毎回ほぼ同じ内容の処理しか書けない。じゃあ、皆さんならどうしたい?

そう、もし〇〇なら□□をさせるみたいな感じのことができると便利だと思っただろう。
それを可能にさせるのがif文、else文と言われるものだ。
長々説明を聞くのも疲れると思うので、早速書いていこう。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        let age = 18
        if (age >= 18){
            console.log("高校卒業おめでとう!!ヾ(_ _)〃 ヾ(≧ー≦)ゞ ヾ(_ _)〃イェーイ");
        }
        else {
            console.log("残りの高校生活青春しようぜ!!");
        }      
    </script>
</body>
</html>

このプログラムでは、ageが18以上なら「高校卒業おめでとう(以下略)」、それ以外(つまり18より下)なら「残りの高校生活青春しようぜ!!」と表示されるようになっている。是非自分でコードを書き換えて試してみて欲しい。

age が18以上の時

image.png

そうではないとき

image.png

簡単にまとめると、if文は特定の条件を満たしたなら処理を実行したい場合、else文はif文で書いた条件を満たさなかったら処理を実行したい時に使う。

ちなみに、不等号は < と > を使って表し、等号つき不等号は >=<= の様に書く。
等号は、= ではなく ==または、===を使う。

繰り返し(for文)

唐突だが、HelloWorldという文字を3回表示させたかったら、あなたならどう書く?
2回コピペしてコードを書き足す方法もあるかもしれない。
でも、もしこれが10000回になったら?
10000回近くコピペを繰り返すのは正直めんどくさい。だからといって、コピペ専門のアルバイトを雇うのはもっとめんどくさい。
そんな時に、繰り返し(for文)を使う。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>繰り返し</title>
</head>
<body>
    <script>
        for (i = 0; i < 4; i++){
            console.log(i + ":HelloWorld")
        }
    </script>
</body>
</html>

image.png

このプログラムでは、iの値:HelloWorldという文字列を4回表示させている。
for文では「これは何回目の繰り返しなのか?」を数えるための変数が必要である。
なので、()の中に初期化式、条件式、加算式というものを書く。

初期化式は変数の値を何からスタートさせるかを書くもの。慣例で0を書くことが多いのでそっちで統一した方が楽。
条件式は、繰り返す条件を書くもの。今回はiが4より小さいなら繰り返すという条件。

さて、初期化式と条件式について話したが、この2つだけだとある問題が発生する。
それは、iの値が増えないのでループが永遠に終わらないという問題だ。
これを解決するためには、iの値をループを繰り返す度に変更してやる必要がある。
それが加算式と呼ばれるものだ。今回の場合、iに1を足すという処理をしている。

最後に、繰り返し処理にはwhile文と呼ばれるものを使う方法もある。今回は、内容簡略化のため説明を省くが必要に応じて使い分けが必要な文法なので、是非各自で調べてみて欲しい。

関数

プログラムを書く際、似たような処理が何度も必要になる場面があるかもしれない。そのような時、関数を作れば独自で機能を追加してプログラムを使い回すことができる。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>関数</title>
</head>
<body>
    <script>
        function average(A,B,C){
            let average = (A + B + C) / 3;
            return average;
        }
        console.log("平均は" + average(3,4,8));
        console.log("平均は" + average(6,4,8));
        console.log("平均は" + average(3,10,8));
    </script>
</body>
</html>

image.png

関数を作るときは、function 関数名(引数){処理}という書き方をする。また、関数を使うときは関数名(引数);と書く。
引数とは、関数に渡す値のこと。今回は、受け取った3つの値の平均を求め、averageという変数に代入している。そして、return文を使って、その値を返すというのが今回作った関数だ。
少し説明が分かりにくかったと思うので、キャッチボールで例えよう。自分が相手にボールを投げた時が引数を渡した時、そして、受け取ったボールを返す時がその値を関数内で処理をして返す時だ。

image.png

HMTLをJSで操作してみる

ここから先が僕が書きたかった内容。ここまでConsole上で文字や数字を表示してきたが、ついにJSからHMTLを操作してみる。

要素を取得

<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>要素を取得</title>
    <div id="text">
        <p>TextTextText</p>
    </div>
</head>
<body>
    <script>
        let el = document.getElementById("text"); //#textのHTML要素を取得
        console.log(el);
    </script>
</body>
</html>

image.png

JSでHTMLを操作したい場合、まず特定のidが入った要素を取得するという処理を行う。
その場合、getElementByIdを使えば、要素を取得することができる。

今回のプログラムを簡単に解説すると、getElementByIdで取得したtextというidの要素をelという変数に代入している。そして、それをConsole上に表示した。

使い方

document.getElementById("id名");

指定した要素の中の末尾に挿入

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>指定した要素の中の末尾に挿入</title>
</head>
<body>
    <div id="text">
        <p>TextTextText</p>
    </div>

    <script>
        let el = document.getElementById("text"); //#textのHTML要素を取得し
        let new_el = document.createElement('p');
        new_el.textContent = "New Text";
        el.appendChild(new_el);
    </script>
</body>
</html>

このコードをブラウザで開くと、divタグ内のpタグの文字に加えて、New Textという文字が追加で表示されているはずだ。

createElementを使うと、HTML内に要素(タグ)を追加することができる。
しかし、creteElementだけではプログラムとして不十分だ。
なぜなら、creteElementは新しく要素を生成するだけであってそれにテキストを生成するまではできないからである。

そこで、textContentを使う。
これを使うことで、生成した要素内にテキストを生成することができる。
そして、pタグ内にNew Textという文を生成したが、このままではこの要素をどこに挿入するかが決まらない。
そこで、appendChildを使う。これによって、どこに挿入するのかが決まる。
ちなみに、appendChildは親要素の最後だ。他にも、insertBeforeなどで要素の挿入場所を決めることができる。

少し応用編

繰り返し処理の話で、HTML内に同じテキストを大量に追加したいときを例として出していたので、実際にやってみたいと思う。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>少し応用</title>
</head>
<body>
    <div id="text">
        <p>TextTextText</p>
    </div>

    <script>
        let el = document.getElementById("text"); //#textのHTML要素を取得してConsoleに表示
        for(i = 0; i < 100; i++){
        let new_el = document.createElement('p'); //pタグを生成
        new_el.textContent = "New Text"; //pタグ内にテキストを追加
        el.appendChild(new_el); //#textの末尾に挿入
        }
    </script>
</body>
</html>

image.png

表示の関係で全て表示されていないが、このプログラムではNew Textという文字を100回生成させている。
このように、JSで少しHTMLを操作できるだけで自分でコピペ作業をしたり、コピペ専用のバイトを雇う必要がなくなるのでかなり便利だ。

イベント処理

例えば、ボタンを押したらテキストを追加したり、アニメーションを追加するなど何かしらイベントを発生させたい時どうすればいいと思う?

そんな時に、いくつかイベント処理をさせる方法がある。
今回は、その一例としてクリックした時の処理を書いていく。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>イベント処理</title>
</head>
<body>
    <button id="btn">クリック</button>
    <div id="text">
    </div>

    <script>
        let el = document.getElementById("text"); //#textのHTML要素を取得
        let btn = document.getElementById("btn"); //#btnのHTML要素を取得

        //btnがクリックされたらこの関数を実行
        btn.onclick = function() {
        let new_el = document.createElement('p');
        new_el.textContent = "ボタンを押したらテキストが増える";
        el.appendChild(new_el);
        };
    </script>
</body>
</html>

onclickを使うと、クリックされた時に行う処理を書くことができる。今回は、ボタンを押す度にテキストが増えるプログラムを書いた。もう少し補足すると、btnというidの要素がクリックされた時に、textというidの要素に対して<p>ボタンを押したらテキストが増える</p>という要素が追加されるという処理だ。

最後に

自分でも気が遠くなるくらいの分量なのに最後まで読んでくれた方ありがとうございました。

今回は、基礎の基礎の部分を知って欲しかったので、HTML・CSSから始めてJSの基本的な文法を少し紹介して終わってしまいました。なので、次回はアニメーションやもう少し応用的な内容に入りたいと思っています。また、今回の内容で触ることができなかったのでフレームワークと言われるものにも少しだけ触れてみる予定です。

最後に、訂正・質問もしくはこんな内容入れて欲しいなどあればコメント欄か、僕のTwitterの方にコメントくれると嬉しいです

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