20210413のJavaScriptに関する記事は26件です。

【JavaScript】 クラスの仕組みを考える

クラスの定義 オブジェクト指向のプログラミング言語における、モノ(オブジェクト)を作成する際に必要なものです。 実装方法は、2パターンあります。 クラス宣言 先頭に「class ⚪︎⚪︎」 と宣言する方法 クラス式 クラスを値として定義する方法で、クラス名は定義してもしなくても良い方法 クラス宣言について まずクラス名を設定するには、基本的に大文字から始めるルールというあります。 そしてそのクラスの中には、コンストラクター(constructor)が存在します。 コンストラクターとは、クラスで生成されたインスタンスを初期化するメソッドのことで、1つのクラスの中で1度だけ使用することが可能です。 クラスからインスタンスを生成するには、必ずnew演算子を使用する必要があります。new を宣言して、クラス内でインスタンス作成することをインスタンス化と呼びます。 今回は、プロフィールを例にして考えてみます。 class Profile { // インスタンスを初期化する constructor(name, age) { // コンストラクター内の記述のことを、インスタンスプロパティと呼ぶ this.name = name; this.age = age; } } // new を宣言してインスタンス作成(インスタンス化) const profA = new Profile('Taro', 15) const profB = new Profile('Hanako', 20) console.log(profA); console.log(profB); // 結果は... Profile { name: 'Taro', age: 15 } Profile { name: 'Hanako', age: 20 } クラス式 名前付きクラスの名前は、クラス内のローカルとして扱われます。(ただし (インスタンスのではなく) クラスの name プロパティによって取得可能) (参照元) MDN Web Docs この説明だけでは少し分かりにくかったので、参照元にも掲載されていますが、私も手元で確かめてみることにしました。 クラス名を定義する書き方 const Profile = class ProfileA { constructor(name, age) { this.name = name; this.age = age; } } console.log(Profile); console.log(Profile.name); // 結果は... [class ProfileA] ProfileA クラス名を定義しない書き方 const Profile = class { constructor(name, age) { this.name = name; this.age = age; } } console.log(Profile); console.log(Profile.name); // 結果は... [class Profile] Profile 実際に手元で確かめてみることにしました。 クラス名を定義した場合には、定数名Profileの値を出力すると、クラス名のProfileAが表示されます。 クラス名を定義しない場合には、定数名Profileの値を出力すると、そのままProfileが表示されます。 参照元の文章は少し難しく感じましたが、試してみて目で確認すると実際は難しいことを言っていないのがよく分かります。 終わり 今回は以上になります。 最近JavaScriptを触れ始めたのですが、これはどういう意味?、何が起きている?と混乱の日々です。小さな気づきや得たことを忘れないように、サボり気味だったQiitaさんにまた少しずつ投稿いていきたいと思います。 また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日本語(カタカナ)を音節単位で分かち書き【Javascript】

概要 カタカナを音節に分けるJavascriptプログラムを書きました。 基本は以前書いた記事のJavascript版になりますが、より正確に音節を区切れるように一部改良しています。 音節とは 日本語の音韻の代表的な分割単位としてモーラと音節があります。モーラはいわゆる俳句の「5・7・5」を数えるときの区切り方で、長音(ー)、促音(ッ)、撥音(ン)も1拍と数えます。それに対し、音節では長音、促音、撥音(以降、これらをまとめて特殊モーラと呼びます)は単体で数えられず、直前の単一で音節となれるカナと合わせてひとつの拍と見なされます。「バーン」のように特殊モーラが連続した場合は3以上のモーラ数で1音節となります。 元文 モーラ分かち書き 音節分かち書き ガッキューシンブン ガ/ッ/キュ/ー/シ/ン/ブ/ン ガッ/キュー/シン/ブン アウトバーン ア/ウ/ト/バ/ー/ン ア/ウ/ト/バーン 参考 音節 - Wikipedia モーラ - Wikipedia 音節分かち書きの考え方 基本的には、特殊モーラをその直前のカナ(モーラ)とひとまとまりとし、それ以外の部分はモーラの単位で分かち書きをすればよいです。ただし特殊モーラが連続した場合の扱いには少し注意が必要です。 また「カア」が「カー」と発音されるように、母音はときどき長音として発音されることがあります。そこでこのような母音も直前のカナと1つの音節としたほうが直感にあう分け方になります。 そこで今回は特殊モーラと母音を考慮した音節分かち書きの実現を目指します。 なお、日本語では、カナ表記とそれを実際にどう発音するかは必ずしも厳密に決まっていません。 例えば「オオウ(Ohhh/奥羽)」は「オーウ」と1音節と考えることも「オオ」「ウ」のように2音節と考えることもどちらもできます。 参考(カナと発音の対応が曖昧な単語の例が示されています) https://qiita.com/hypnos/items/4f31dff6241a907fba9b 厳密に対処するためには、単語ごとの発音辞書のようなものを持つことが有効ですが、今回はカタカナの情報しか得られない前提の元、ある程度割り切って、実装します。 今回の実装が必ずしもすべての場合における正解ではないことに注意してください。 モーラ分かち書き 音節分かち書きの前に、その基本となるモーラ分かち書きについて考えます。 モーラ分かち書きでは2文字で1モーラを形成するカナ(キャ、キュ、キョなど)があることに注意し、2文字カナ、1文字カナの順番でマッチすることによって実現できます。 2文字1モーラのカナを形成するパターンは以下のとおりです。 正規表現 意味 例 ① [ウクスツヌフムユルグズヅブプヴ][ァヮィェォ] ウ段+「ァ/ヮ/ィ/ェ/ォ」 ウァ、フィ ② [キシシニヒミリギジヂビピ][ャュェョ] イ段(「イ」を除く)+「ャ/ュ/ェ/ョ」 キャ、シェ ③ [テデ][ャィュョ] 「テ/デ」+「ャ/ィ/ュ/ョ」 テャ、デュ 1文字カナは[ァ-ヴー]の正規表現でマッチ可能です。 2文字のカナから順番にマッチすることに注意して、モーラ分かち書き関数を以下の通り実装できます。 //入力カナをモーラの単位で分かち書きする。 function moraSplit(text){ let kana = getKanaPattern(); let re = /[ウクスツヌフムユルグズヅブプ][ァヮィェォ]|[キシチニヒミリギジヂビピテデ][ャュェョ]|[テデ]ィ|[ァ-ヴー]/g; text = text.match(re);//match関数で抽出することで分かち書きと同一の結果を得る return text; } 出力は以下のような感じです。 console.log(moraSplit("ガッキュウシンブン")); //["ガ", "ッ", "キュ", "ウ", "シ", "ン", "ブ", "ン"] console.log(moraSplit("アウトバーン")); //["ア", "ウ", "ト", "バ", "ー", "ン"] 音節分かち書き 次に音節分かち書きについて考えます。 前処理 入力可能な全てのカナのパターンに対して分け方のルールを決めようとすると非常に複雑になってしまうので、今回は日本語的に不自然でないカタカナの並びに限ることとします。 具体的には以下を不自然なカナと定義します。 また、これを修正する関数を作っておきます。 不自然な促音、長音の削除 長音の連続や促音の連続は発音上、一意な意味がないので削除します。 促音のあとの長音と、文頭の促音長音も同様に、発音に与える一意な意味がないので、削除します。 function removeBarAndSokuonReputation(text){ text = text.replace(/ー+/g,"ー");//ーの連続を1文字にする text = text.replace(/(?<=ッ)[ーッ]+/g,"");//ッの後ろのーまたはッの連続を削除 text = text.replace(/^[ーッ]+/g,"")//先頭の[ーッ]を削除 return text; } (function(){ let text = "ーーッーーアーーーッッーンコーーーモチ"; console.log(removeBarAndSokuonReputation(text)); //アーッンコーモチ })(); 不自然な小文字の修正 小文字カナは「ッ」と2文字1モーラのカナの一部である場合を除き、登場させないようにしたいです。 まず長音の後ろの小文字母音は削除します。(例:「アーァ」→「アー」) また長音直前のカナと同じ種類の母音となる小文字母音は長音に変換します(例:「カァ」→「カー」) このために、カナ1文字を母音に変換する関数を作ります。その後、小文字母音の直前のカナと小文字母音の母音が一致するかどうかを調べて変換します。 //文字を母音に変換 function charToVowel(char){ if(char == "ー" ){ //console.log("warning: only ー is input"); return char; } //伸ばし棒を除いた末尾の文字を取得 let last = char[char.length-1]; for(let i=char.length-1;i>-1;i--){ last = char[i]; if(last != "ー")break; } let rows = { "ア":"アカサタナハマヤラワガザダバパァャヮ", "イ":"イキシチニヒミリギジヂビピィ", "ウ":"ウクスツヌフムユルグズヅブプヴゥュ", "エ":"エケセテネヘメレゲゼデベペェ", "オ":"オコソトノホモヨロゴゾドボポォ", "sp":["sp","ン","ッ"] } let vowel = last; for(let v in rows){ let row = rows[v]; if(row.includes(last)){ vowel = v; break; } } return vowel; } //小文字母音を長音に変換 function smallVowelToBar(text){ //長音のうしろの小文字母音を長音に let replaced_text = text.replace(/(?<=ー)(ァ+|ィ+|ゥ+|ェ+|ォ+)/g,""); //同じ母音のカナの後ろの小文字母音を長音に replaced_text = replaced_text.replace(/[ァ-ヴ](ァ+|ィ+|ゥ+|ェ+|ォ+)/g,function(match){ let res = match; let l2s = { "ア":"ァ", "イ":"ィ", "ウ":"ゥ", "エ":"ェ", "オ":"ォ" } //1文字目の母音が2文字目の小文字母音と対応していたら let first_vowel = charToVowel(match[0]); if(first_vowel in l2s && l2s[first_vowel] == match[1]){ res = match[0]+"ー"; } //エィやオゥも長音に変換したい場合はコメントを外す //else if(first_vowel == "エ" && match[1] == "ィ"){ // res = match[0] + "ー"; //}else if(first_vowel == "オ" && match[1] == "ゥ"){ // res = match[0] + "ー"; //} //上記以外の小文字母音の連続に対応(これがないと「ヴァァァ」の「ァァァ」などが残り続ける else if(match.length>=3){ res = match[0]+match[1]+"ー"; } return res; }); return replaced_text; } (function(){ let text = "キィィアァァヴァァァ"; console.log(smallVowelToBar(text)); //キーアーヴァー })(); 次に2文字カナの一部でない小文字を大文字にします。 //2文字カナの一部でない小文字(ッを除く)を大文字にする function smallVowelToLarge(text){ let re_a = "(?<![ウクスツヌフムユルグズヅブプヴ])[ァヮェォ]";//ウ段の後ろ以外のァヮェォ let re_u = "(?<![トド])ゥ"; let re_y = "(?<![キシチニヒミリギジヂビピテデ])[ャュョ]";//イ段の後ろ以外のャュョ let re_i = "(?<![ウクスツヌフムユルグズヅブプヴテデ])[ィ]";//テデの後ろ以外のィ let re = [re_a,re_y,re_u,re_i].join("|");//上記のいずれかにマッチさせる let s2l = {"ァ":"ア","ィ":"イ","ゥ":"ウ","ェ":"エ","ォ":"オ","ヮ":"ワ","ャ":"ヤ","ュ":"ユ","ョ":"ヨ"} //マッチした小文字を大文字にして返す let replaced_text = text.replace(new RegExp(re,"g"),function(match){ let large = s2l[match]; return large; }); return replaced_text; } (function(){ let text = "キィンィファァキャヴァァトゥァ"; console.log(smallVowelToLarge(text)); //キーアーヴァー })(); 不自然なカナの並びを修正する関数 上記、促音、長音、小文字に関する処理を組み合わせて、不自然なカナの並びを修正する関数を作成します。 小文字を長音に変換する処理がある関係で、小文字に関する処理を先に実施する必要があることに注意してください。 //小文字や長音、促音の不自然な並びを解消する function formatText(text){ text = smallVowelToBar(text); text = smallVowelToLarge(text); text = removeBarAndSokuonReputation(text); return text; } (function(){ let text = "ーキィーーンィーーーッーーファァァィオゥキャヴァァトゥァ"; console.log(formatText(text)); //キーンイファーキヤヴァートゥア })(); 母音ごとのカナパターンの生成 音節分かち書きのために使うカナのパターンを文字数ごと、母音ごとに生成する関数を作っておきます。 文字数ごとに取得するのは、正規表現で長いカナから順にマッチする必要があるからです。 母音ごとに取得するのは、あとで「カア」などのように実質的に長音とみなせる母音を見つけるためです。 //日本語のカナの正規表現パターンをア段からオ段の音(と全部)に分けて取得 //日本語の場合、「ファ」などのように2文字で1モーラを構成するカナがあることに注意 function getKanaPattern(){ //ア段からオ段までの1文字カナ集合と「テ」「デ」の集合を定義 let kana_a = "[アカサタナハマヤラワガザダバパ]"; let kana_i = "[イキシチニヒミリギジヂビピ]"; let kana_i2 = kana_i.replace("イ","");//ャュョとくっつける用のイ段 let kana_u = "[ウクスツヌフムユルグズヅブプヴ]"; let kana_e = "[エケセテネヘメレゲゼデベペ]"; let kana_o = "[オコソトノホモヨロヲゴゾドボポ]"; let kana_td = "[テデ]"; let kana_td2 = "[トド]"; //2文字で1モーラになるカナの定義 let kana_multi_a = "("+[kana_u+"[ァヮ]",kana_i+"ャ",kana_td+"ャ"].join("|")+")"; let kana_multi_i = "("+[kana_u+"ィ",kana_td+"ィ"].join("|")+")"; let kana_multi_u = "("+[kana_i+"ュ",kana_td+"ュ",kana_td2+"ゥ"].join("|")+")"; let kana_multi_e = "("+[kana_u+"ェ",kana_i+"ェ"].join("|")+")"; let kana_multi_o = "("+[kana_u+"ォ",kana_i+"ョ"].join("|")+")"; let kana_multi = "("+[kana_u+"[ァィェォ]",kana_td+"[ャィュョ]",kana_i+"[ャュョ]"].join("|")+")"; //ンーッと小文字を除くカナ let kana_single_base = "[アイウエオ-ヂツ-モヤユヨ-ロワヲヴ]"; //2文字で1モーラとなるカナも含めた全カナ集合(ー/ン/ッと小文字単体は除く)の定義 let kana_base = "("+[kana_multi, kana_single_base].join("|")+")"; //2文字で1モーラとなるカナも含めた全カナ集合(ー/ン/ッと小文字単体も含む)の定義 let kana_all = "("+[kana_multi, "[ァ-ヴー]"].join("|")+")"; return { "base":kana_base,//ン、ー、ッと小文字を除く2文字および1文字カナ "all":kana_all,//ン、ー、ッと小文字も含む2文字および1文字カナ "multi_a":kana_multi_a, "multi_i":kana_multi_i, "multi_u":kana_multi_u, "multi_e":kana_multi_e, "multi_o":kana_multi_o, "multi":kana_multi, //2文字1モーラのカナ全体 "single_a":kana_a, "single_i":kana_i, "single_u":kana_u, "single_e":kana_e, "single_o":kana_o, "single_td":kana_td, "single_td2":kana_td2, "single_base":kana_single_base //ン、ー、ッと小文字を除く1文字カナ } } console.log(JSON.stringify(getKanaPattern())); {"base":"(([ウクスツヌフムユルグズヅブプヴ][ァィェォ]|[テデ][ャィュョ]|[イキシチニヒミリギジヂビピ][ャュョ])|[アイウエオ-ヂツ-モヤユヨ-ロワヲヴ])","all":"(([ウクスツヌフムユルグズヅブプヴ][ァィェォ]|[テデ][ャィュョ]|[イキシチニヒミリギジヂビピ][ャュョ])|[ァ-ヴー])","multi_a":"([ウクスツヌフムユルグズヅブプヴ][ァヮ]|[イキシチニヒミリギジヂビピ]ャ|[テデ]ャ)","multi_i":"([ウクスツヌフムユルグズヅブプヴ]ィ|[テデ]ィ)","multi_u":"([イキシチニヒミリギジヂビピ]ュ|[テデ]ュ|[トド]ゥ)","multi_e":"([ウクスツヌフムユルグズヅブプヴ]ェ|[イキシチニヒミリギジヂビピ]ェ)","multi_o":"([ウクスツヌフムユルグズヅブプヴ]ォ|[イキシチニヒミリギジヂビピ]ョ)","multi":"([ウクスツヌフムユルグズヅブプヴ][ァィェォ]|[テデ][ャィュョ]|[イキシチニヒミリギジヂビピ][ャュョ])","single_a":"[アカサタナハマヤラワガザダバパ]","single_i":"[イキシチニヒミリギジヂビピ]","single_u":"[ウクスツヌフムユルグズヅブプヴ]","single_e":"[エケセテネヘメレゲゼデベペ]","single_o":"[オコソトノホモヨロヲゴゾドボポ]","single_td":"[テデ]","single_td2":"[トド]","single_base":"[アイウエオ-ヂツ-モヤユヨ-ロワヲヴ]"} 促音、長音、撥音を直前のカナとくっつける 特殊モーラが連続したとき、1つの音節として扱うかどうかを考えます。 カナに特殊モーラが1文字続く場合、例えば「アッ」「アー」「アン」続いたときは1音節として扱います。 2文字続く場合、「アーッ」「アーン」「アンッ」は1音節とするのが自然に思えます。 一方で「アッン」は「ン」で言い直す読み方をすることが多そうなので、「アッ」「ン」と分けることにします。 「アンー」は微妙なところなので保留します。 「アッー」は前処理が適切に行われていれば登場しない並びです。 3文字続く場合、「アーンー」は「アー」「ンー」とそれぞれ発音することが多そうなので、2音節に分けます。 「アーンッ」も「ンッ」で言い直しているきがするので「アーンッ」で分けます。 ここから「ン」のうしろに「ー」や「ッ」が来るときは「ンー」「ンッ」のように「ン」始まりで音節を分けるというルールがつくれます。 ここまで考えて、保留していた「アンー」については、「アーンー」の分け方と統一感を持たせるために「ア」「ンー」とすることにします。こうすると「アンーッ」も「ア」「ンーッ」で分けることに迷いがなくなり、都合がいいです。 また前処理により促音と長音は2連続しませんが、撥音は連続しえます。今回は「アンン」のように「ン」が連続したときは「アン」「ン」と「ン」を一つずつ分けて音節とします。「ンン」の発音は実際には「ンー」になることもあるので、完全に正確ではありませんが、「ンー」を意味したいときは「ンー」と入力してもらうことを期待して、このように処理します。 表にまとめると以下のようになります。 例 処理 アッ 1音節 アー 1音節 アン 直後に「ー」がない限り1音節 アッン 「アッ」「ン」の2音節 アーッ 1音節 アーン 直後に「ー」「ッ」がない限り1音節 アンッ 1音節 アンー 「ア」「ンー」の2音節 アンン 「アン」「ン」の2音節 アーンッ 「アー」「ンッ」の2音節 アーンー 「アー」「ンー」の2音節 以上から特殊モーラが3文字以上連続すると必ず2音節以上に分かれるので、以下のように2文字以下の検出パターンを定義すると、これらを上手くマッチさせることができます。 //「ッ」「ー」「ン」が2文字続いたときのパターン。「ーン」は後ろに「ー」「ッ」が来るとき以外 let re2 = "ーッ|ンッ|ーン(?![ーッ])"; //「ッ」「ー」「ン」が2文字続いたときのパターン。「ン」は後ろに「ー」が来るとき以外 let re1 = "ー|ッ|ン(?!ー)";//ンは後ろに長音が来るとき以外 //「ン」が音節の頭として登場したときのマッチ let re_n_bar = "ン([ーッ]|ーッ)"; 母音を直前のカナとマッチさせる カナの後ろにその母音が続いていた場合、その母音は実質的に長音とみなせるため、合わせて1音節とします(例:「カアカア」→「カア」「カア」)。また「エイ」「オウ」のようなエ段+イ、オ段+ウも同様に長音的に扱います(「コウセイ」→「コウ」「セイ」) ただし、母音の後ろに特殊モーラがあれば、母音とそれらの音をマッチさせることを優先するようにします。(例:「カアー」→「カ」「アー」)。これはこのようにしたほうがプログラムが書きやすいというのと、音節を分けたくない場合は母音を長音に変換してから入力するようにすれば済む(つまりユーザにどう音節を分けたいかの自由度を与えられる)というのが理由です。 同じ母音が2回ずつ続いた場合は、2文字ずつで音節を構成させるようにします(例:「アアアアア」→「アア」「アア」「ア」) これはカナをア段からオ段に分けて、それらと母音の並びをマッチすることで検出します。 余った文字を1モーラずつ分ける 「ッ」「ー」「ン」や母音とマッチしなかった部分は1モーラずつ分離します。 長い塊から順にマッチさせる 上記までのマッチを長い順から順に適用することで音節分かち書きを実現します。 具体的には以下の順でマッチをします。 実際の関数ではより確実にするために適宜例外処理も入れるようにしています。(「ー」「ッ」が後ろに来る「アーン」はマッチしない、など) 2文字カナ+特殊モーラ (例:ヴァーン、キャッ) 2文字カナ+母音(例:ジョウ、シャア) 2文字カナ(例:ヴァ) 「ン」+促音/長音(例:ンーッ、ンー) 1文字カナ+特殊モーラ (例:アーッ、カン) 1文字カナ+母音(例:カア) 1文字カナ(例:ア) これを関数にしたのが以下です。 function syllableSplit(text){ //よく使うカナパターンの取得 let kana = getKanaPattern(); //ーンッを前のカナとつなげるときのパターン let re2 = "ーッ|ンッ|ーン(?![ーッ])";//ーンは後ろにーッが来るとき以外 let re1 = "ー|ッ|ン(?!ー)";//ンは後ろに長音が来るとき以外 let re_back = "("+[re2,re1].join("|")+")"; //長いものからマッチする //2文字カナとーンッのマッチ let re_multi_bar = "("+kana["multi"] + re_back + ")"; //2文字カナと母音のマッチ let re_multi_a = kana["multi_a"]+"ア"; let re_multi_i = kana["multi_i"]+"イ"; let re_multi_u = kana["multi_u"]+"ウ"; let re_multi_e = kana["multi_e"]+"[エイ]"; let re_multi_o = kana["multi_o"]+"(オ|ウ(?![ァィゥェォ]))"; let re_multi_vowel = "("+[re_multi_a,re_multi_i,re_multi_u,re_multi_e,re_multi_o].join("|")+")"; re_multi_vowel += "(?![ーンッ])"; //2文字カナ単独のマッチ let re_multi_unit = kana["multi"]; //ンとーッのマッチ let re_n_bar = "ン([ーッ]|ーッ)"; //1文字カナとーッンのマッチ let re_single_bar = "("+kana["single_base"]+re_back+")"; //1文字カナと母音のマッチ let re_single_a = kana["single_a"]+"ア"; let re_single_i = kana["single_i"]+"イ"; let re_single_u = kana["single_u"]+"ウ(?![ァィェォ])"; let re_single_e = kana["single_e"]+"[エイ]"; let re_single_o = kana["single_o"]+"(オ|ウ(?![ァィェォ]))"; let re_single_vowel = "("+[re_single_a,re_single_i,re_single_u,re_single_e,re_single_o].join("|")+")"; //1文字カナ単独のマッチ re_single_vowel += "(?![ーンッ])"; let re_single_unit = "[ァ-ヴー]"; //上記で定義した条件のオアをとる let re = [re_multi_bar, re_multi_vowel, re_multi_unit, re_n_bar, re_single_bar, re_single_vowel, re_single_unit].join("|"); //matchで抽出 text = text.match(new RegExp(re,"g")); return text; } 出力を確認してみます。 (function(){ let texts = [ "アウトバーン", "ーーッアッッウートンッバァァァーーンンッ", "コウチョウセンセイ", "ガッキュウシンブンノエイセイテキカンテン", "オウトクチュウル", "チューンナップ", "ピュビュッパラッパンッパーンェィ", "イェェェェーーーーィイッ" ] for(let text of texts){ let formatted = formatText(text); let splitted = syllableSplit(formatted); console.log("raw",text); console.log("formatted",formatted); console.log("splitted",splitted); console.log("formatted==splitted", formatted == splitted.join("")); } })(); raw アウトバーン formatted アウトバーン splitted ["ア", "ウ", "ト", "バーン"] formatted==splitted true raw ーーッアッッウートンッバァァァーーンンッ formatted アッウートンッバーンンッ splitted ["アッ", "ウー", "トンッ", "バーン", "ンッ"] formatted==splitted true raw コウチョウセンセイ formatted コウチョウセンセイ splitted ["コウ", "チョウ", "セン", "セイ"] formatted==splitted true raw ガッキュウシンブンノエイセイテキカンテン formatted ガッキュウシンブンノエイセイテキカンテン splitted ["ガッ", "キュウ", "シン", "ブン", "ノ", "エイ", "セイ", "テ", "キ", "カン", "テン"] formatted==splitted true raw オウトクチュウル formatted オウトクチュウル splitted ["オウ", "ト", "ク", "チュウ", "ル"] formatted==splitted true raw チューンナップ formatted チューンナップ splitted ["チューン", "ナッ", "プ"] formatted==splitted true raw ピュビュッパラッパンッパーンェィ formatted ピュビュッパラッパンッパーンエイ splitted ["ピュ", "ビュッ", "パ", "ラッ", "パンッ", "パーン", "エイ"] formatted==splitted true raw イェェェェーーーーィイッ formatted イエーイッ splitted ["イ", "エー", "イッ"] formatted==splitted true おわりに 日本語カナを可能な限り丁寧に音節に分ける関数をJavascriptで作ってみました。 ホントは小文字カナも長音に直さずにちゃんと扱いたかったのですが、正規表現の管理がしきれなくなってきたので諦めました。なお今回よりも簡単にしたければ、大文字母音も長音に直してしまってから、特殊モーラのみをどう扱うかに注力するのもありかと思います。 音節の分け方は一意ではないので、今回の実装が唯一の正解ではないことに注意してください。 もしよりよい分け方があれば教えていただけると嬉しいです。 今後の課題をメモとして残しておいて、本記事を終わります。 ここまでお読みいただきありがとうございました。 単語ごとの発音辞書の利用。特に「エイ」の発音が「エー」、「エイ」、「エ」「イ」のどれなのかを正しく区別できると嬉しいです。例えば「衛生」の「エイ」は「エー」とするが、魚介類の「エイ」は「エイ」(または「エ」「イ」)とするなどです。 漢字読み方辞書の利用。単語ごとの発音辞書を用意するまでせずとの、漢字の読み方辞書があれば、音節の切れ目の推測の確度をある程度高めることができそうです。例えば、「講師」であれば「コー」「シ」、子牛が「コ」「ウシ」と、漢字の読み方の切れ目に沿って音節をわける処理を入れることなどが考えられます。 母音が省略されるカナの処理。例えば「です」という日本語は「des」のように、末尾の母音が脱落することで1音節とみなせる発音になることが多いです。アプリケーションによりますが、このように実質的に1音節とみなせるパターンをどんどんプログラムに取り入れることはしてもよいと考えています。 小文字母音の処理。今回、小文字母音は2文字カナの一部となっている場合を除いて登場させないようにしましたが「ィヤー」みたいな表記は日本語的に発音できないわけではないため、こうしたパターンの分かち書きの方法を考えることはある程度有用と考えられます。 特殊モーラや母音の連続の処理。「アンー」は今回は「ア」「ンー」と2音節にしましたが、やはり1音節にしたいときもあります。どういう基準で1音節または2音節とするかはもう少し詰める余地があります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

denoドキュメント リンク集

公式ドキュメント https://deno.land/ : 公式サイト https://deno.land/manual : 公式マニュアル https://doc.deno.land/builtin/stable : ランタイムAPIのドキュメント https://doc.deno.land/builtin/unstable : ランタイムAPIのドキュメント(--unstableフラグを付けて実行すると利用できるもの=不安定API) https://github.com/denoland/deno : リポジトリ https://github.com/denoland/deno/issues?q=sort%3Aupdated-desc : issueのリスト(バグ報告とか) https://github.com/denoland/vscode_deno : 公式が提供するvscode拡張 標準ライブラリ https://deno.land/std : 公式サイト https://github.com/denoland/deno_std : リポジトリ https://golang.org/pkg/ : denoの標準ライブラリはGOの標準ライブラリがベースになっている。 https://doc.deno.land/ : 公式ドキュメントジェネレータ(サードパーティ製ライブラリにも利用できる) サードパーティ製ライブラリ https://deno.land/x/ : 公式ホスティングサービス https://www.skypack.dev/ https://unpkg.com/ https://jspm.org/ https://esm.sh/ https://github.com/ : githubに置いたjs/tsファイルを、rawボタンのリンク先URLを使って実行できる ※denoはURLから直接importできるので、npmに相当するレジストリがたくさんある コミュニティ https://discord.gg/deno : discord。公式。コアチームのメンバーも参加している。 https://gitter.im/denolife/Lobby : 過去に公式で使われていたコミュニティ。現在はdiscordに移行済み。 https://scrapbox.io/deno-ja/Slack%E3%81%AE%E5%8F%82%E5%8A%A0%E6%96%B9%E6%B3%95 : slack。日本語のコミュニティ https://github.com/denolib/awesome-deno : コミュニティ作成のリンク集。情報たくさん!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者でもわかる】jsでクリックした数をカウントする装置

どうも7noteです。カウンターを作ります。(記録保持機能なし) jsを使った簡易カウンターを作ります。 動きの部分のみのだけなので、ページリロードすればまた0に戻ります。 ソース index.html <p>カウンター:<span id="count">0</span></p> <input type="button" value="カウント" onClick="count();"> script.js function count(){ var thisCount = $("#count").html(); thisCount = Number(thisCount); thisCount = thisCount + 1; $("#count").html(thisCount); } 解説 html側はテキストとボタンを設置しています。カウントするところをわかりやすいようにspanで囲みidを指定します。 var thisCount = $("#count").html();で現状の値を取得。 このままだと文字列のままなので、計算できるようにvar thisCount = Number(thisCount);で文字列から数字列に変換。 var thisCount = thisCount + 1;で取得した値に1を足します。 $("#count").html(thisCount);で値を1足した数字に上書きします。 まとめ わかりやすく書くならこんな感じでしょうか。ただ記録を保持させたり、もっと複雑な計算をさせたり、カウントした前の数字を保管しておいてまた別で流用するなどが想定される場合はこのプログラムでは少ししんどいかなと思います。 おそまつ! ~ Qiitaで毎日投稿中!! ~ 【初心者向け】WEB制作のちょいテク詰め合わせ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Apps ScriptとQRコードで、あれ、どこにしまったっけ?解消

作ったもの 家にあるモノを、どこにしまったか記録・管理する仕組み (Apps Scriptの練習兼ねて作った&作りながら考えたものであり、リファクタリングはしてませんorz) 使い方 初回登録時:モノに貼ったQRコードをスマホで撮影 ⇒ モノの名前を登録 初回登録以降:モノのQRコードを撮影 ⇒ 収納場所のQRコードもスマホで撮影して収納場所を更新 元からバーコードがついている商品は、そのバーコードを読み取って名前登録でもOK 収納場所には、収納場所の名を埋め込んだQRコードが必要 本については、honno.infoからタイトルを取ってくるので、商品名の入力は不要 使ったツール Google Apps Script Googleスプレッドシート iOS ショートカット ラベルプリンター(Brother P-touch 18R):QRコードのラベルが作れる(普通のプリンターを使う場合) honno.info 参考にした&コードを拝借したサイト 続・Webの技術だけで作るQRコードリーダー GoogleスプレッドシートでISBNから書籍のタイトル・出版社名・著者名・発売日・本体価格・判型・表紙画像・バーコード(上段のみ)を表示するための関数とURL(追記あり) 仕組みの大枠 Googleスプレッドシートに、モノと収納場所の一覧を作成 iOSの場合:ショートカットアプリを利用して、QRコード(またはバーコード)を読み取り Androidの場合:JavaScriptベースのQRコードリーダーで読み取り(作成したWebアプリのURLに「?scan=y」を付ける) 読み取った結果をApps Scriptで作成したWebアプリに投げてスプレッドシートを更新 作り方 Googleスプレッドシート:「main」というシートを作成&以下の列を作成(No列は結局使わなかったが。。) [No, item_id, item, place_id, category, datetime, item_internal, Memo, Amazon] 「item_category_master」というシートを作成し、A列にモノのカテゴリ一覧を記載 Googleスプレッドシート:「ツール」→「スクリプトエディタ」を開き、以下の作成ファイル一覧に記載のファイルを作成 スクリプトエディタ内で、「デプロイ」→「新しいデプロイ」 ギアマークから「ウェブアプリを選択」→「デプロイ」をクリックして、「exec」で終わるWebアプリのURLをコピー iOSのショートカットを使って、以下のiOSショートカット例に記載したようなショートカットを作成 作成ファイル一覧 コード.gs : Apps Script 本体 show_result.html : 結果表示のためのHTML register.html:新規にモノを登録したときのため update.html:収納場所を更新した時のため scan.html : 以下QRコードをWebでスキャンするためのコード sw.js.html app.js.html jsQR.js.html app.css.html nothing.html:何も指定されなかったときのダミー iOSショートカット例 QRコードの作り方 Googleスプレッドシートでも以下のような関数で、QRコード作成が可能 プリンターで印刷したものを段ボール等にテープで貼ってます。(A1にQRコードにしたい文字列がある前提) 段ボールなどに貼るときは、名前や番号も一緒に印刷して貼ると識別しやすいです。 =image("http://chart.apis.google.com/chart?chs=200x200&cht=qr&chl=" & A1) 感想とその後の我が家 Apps Script簡単、便利ー。 家のあちこちにQRコードを貼りまくりです。 アイロンで、服にQRコードを付け始める図 P-touch ファブリックテープ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactJSプロジェクトindex.jsとindex.htmlの役割

reactプロジェクト基本構造 npx create-react-appコマンドを使ってreactプロジェクトを生成すると以下のようなフォルダ構成が生成されます。(本記事では説明に必要であるファイルだけ記載しました) src | |ーApp.js |ーindex.css |ーindex.js public | |ーindex.html 上記のファイル(index.js,index.html)はどんな役割かな。。気になって少し気合を入れて調べてみました。 Index.htmlファイル Wikiにspaを検索すると以下のように説明しています。 「単一のWebページのみから構成することで、デスクトップアプリケーションのようなユーザ体験を提供するWebアプリケーションまたはWebサイトである」 単一のwebページのみからの表現が凄くきになりますよね。 気づいた方が多いと思いますがここの単一のwebページはpublicフォルダの下にあるindex.htmlになります。 index.jsファイル Index.jsファイルはreactアプリが実行される際、最初に実行されるjavascriptファイルになります。 少しindex.jsフィアルの中にをみてみましょう! index.js import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root')); index.jsとindex.htmlの関係 ReactDOM.render(<App />, document.getElementById('root’));このコードをしっかり確認する必要があります。 上記のコードを人間が理解できるように翻訳するとドキュメントの中にrootと言うIDを持っているヤツの中にを描画して!くらいの表現になります。 なんとなく分かったけど。。。documentオブジェクトは何処から来る?と思ってませんか。 この謎はindex.htmlフィアルを見ると無くなります。 index.htmlのbodyだけ持ってきました。 index.html <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> divタグを見るとなんか先見たコードと関係があるらしいです。 そうですねえ!!index.jsのdocument.getElementById(‘root’)はindex.htmlの<div id=“root”>を指名しています。 上記のコードを人間が理解できるように翻訳するとドキュメントの中にrootと言うIDを持っているヤツの中にを描画して!表現をもっと詳しくすると コンポナントをindex.htmlの中にelementのidがrootになっているやつの中に描画して!になります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(備忘用)React Refの関数がいつからか変わってた

ReactのRefの関数が一部変わってました。 他にもあるかもしれませんが、備忘用に。 scrollResponderScrollToはレンダリングされたときのスクロール位置を指定できる関数です。 関数自体が変わったのかもしれませんが、動きはいまのところ同じっぽいです。 ※変更になったversionは正確にあってないと思うのでご参考までに。。。 旧(~v16.8.6?) scrollViewRef: React$ElementRef<*> = React.createRef(); render() { < onWillFocus={() => { this.scrollViewRef.current.scrollResponderScrollTo({ // この関数 y: 0, animated: false, }); }} /> } 新(v17.0.1~?) scrollViewRef: React$ElementRef<*> = React.createRef(); render() { < onWillFocus={() => { this.scrollViewRef.current.scrollResponderZoomTo({ // この関数 y: 0, animated: false, }); }} /> }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Chrome拡張] backgroundスクリプトでローカルストレージの値をローカル変数として使う

うまくいかなかったこと Chromeの拡張のchrome.webRequest.onHeadersReceivedでローカルストレージのデータ取得したいけど、なぜかawaitが待たない。。。 本格的にやるならライブラリ使ったりした方が良いが取り急ぎ対応したい。 backgroun.js // リクエストを捕捉するAPI chrome.webRequest.onHeadersReceived.addListener( function () { // ここでローカルストレージのデータ参照したいが、非同期処理がうまく動かない }, { urls: ["<all_urls>"], types: ["main_frame", "sub_frame"], }, ["blocking", "responseHeaders"] ); popup.js // ブラウザアクションのスクリプトでローカルストレージを更新している // フィールド情報をchromeストレージにセットする function setStorage () { // ローカルストレージにセット chrome.storage.local.set({ hogehoge: hogehogeString, pikapika: pikapikaString, }, function(){}); } 非同期処理を書かずに対応する ローカルストレージの変更を検知するリスナーが存在することがわかったので、これを使って変更後の値を変数に設定します。 chrome.storage.onChangedをイベント登録して変更後の値を変数に代入していきます。 backgroun.js var hogehoge = ''; var pikapika = ''; // 読み込み時にローカルストレージの値を変数に設定 chrome.storage.local.get( [ 'hogehoge', 'pikapika' ], function(storage){ hogehoge = storage.hogehoge; pikapika = storage.pikapika; } ); // ローカルストレージの変更を検知する chrome.storage.onChanged.addListener(function(changes, area) { // ローカルストレージの変更か? if (area !== "local") { return; } // changesは変更したプロパティの配列 if ("hogehoge" in changes) { hogehoge = changes.hogehoge.newValue; // 変更前の値はoldValueプロパティに持つ } if ("pikapika" in changes) { pikapika = changes.pikapika.newValue; } }); // リクエストを捕捉するリスナー chrome.webRequest.onHeadersReceived.addListener( function () { alert(pikapika + hogehoge); }, { urls: ["<all_urls>"], types: ["main_frame", "sub_frame"], }, ["blocking", "responseHeaders"] ); これでbackgroundで動くスクリプトの変数とローカルストレージの値を同期できます。 無駄が多いがロジック部分にコールバックなど非同期処理のコードがなくなるし、わりとありか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

変数名のルールとよく使われる命名記法②

変数名の命名記法 プログラムの中では変数名の名前の付け方が統一されていると解りやすい 一般的に使用されている命名記法には3つの種類がある キャメルケース 別名:ローワーキャメルケース 特徴 最初の単語は全て小文字 2つ目の単語は先頭の文字を大文字にして、他は小文字にする 参考例 let userName; let totalScore; パスカルケース 別名:アッパーキャメルケース 特徴 全ての単語の先頭文字を大文字 他は全て小文字にする 参考例 let UserName; let TotalScore; スネークケース 特徴 全ての単語を小文字 単語と単語をアンダーバー_で繋げる 参考例 let user_name; let total_score; 定数 特徴 全ての文字を大文字 単語と単語をアンダーバー_で繋げる 参考例 const MAX_SIZE = 10; 補足情報 他にも単語と単語をハイフン-で繋げる ケバブケースがあるが、JavaScriptでは-が 使用できないので使用されない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

静的に入力されたtableの値と、Chart.jsを連携してみた

ある案件で、tableに入力された値と、Chart.jsを連携する実装がありました。 今回は円グラフを使用しましたが、他のグラフでも応用可能だと思います。 参考になれば、幸いです。 はじめに table構造はとてもシンプルです。 (実際の運用では、それぞれの数値に関してhtmlは触らずに、WordPressのカスタムフィールドでの運用を想定しています。) See the Pen table - basic by dan (@___dan) on CodePen. このtableを元に、以下の2つのパターンを実装しました。 カテゴリ毎の合計値 (tableでは年度別の合計値が出力されているが、Chart.js上ではカテゴリ毎の合計値を出力する。) 対合計比 (総計を基準にし、各カテゴリの合計値が何%なのかを出力する。) 実装編 カテゴリ毎の合計値 See the Pen table(total) - chart.js by dan (@___dan) on CodePen. 記述したjsは以下の通りです。 (trをscope範囲とし、それぞれの数値が記入されているtdをtargetとする。) //空の配列を用意 let total = []; $('.scope').each(function () { let target = $(this).find('.target'); let sum = 0; for (let i = 0; i < target.length; i++) { //取得したtextが文字列のため、parseIntで数値型に変換 //sum = $(target[i]).text() * 1 + sum;のように、1を掛けて数値型に変更するのもアリかと思います。 sum = parseInt($(target[i]).text()) + sum; } //push()メソッドを使用して、sumの値を配列でtotalへ返却 total.push(sum); }); console.log(total); //実行結果[60, 30, 6, 96] - カテゴリ毎の合計値が配列に格納 Chart.js内のdataに、作成したtotal変数をセットしてあげて完成です。 data: total なお、optionsの値がブラウザで確認したところ正常に動いていたのですが、 codepenで確認すると丸々抜けているようです、、、。 (本来であれば、円グラフの上に「進学先 人数」というタイトルと、ホバーしたときのラベルが〇〇人と表示されます。) options: { title: { display: true, text: '進学先 人数' }, tooltips: { callbacks: { label: function (tooltipItem, data) { var label = data.labels[tooltipItem.index]; if (label) { label += ': '; } label += data.datasets[0].data[tooltipItem.index] + '人'; return label; } } } } 対合計比 See the Pen table(ratio) - chart.js by dan (@___dan) on CodePen. ※こちらもoptionsの値が動いておりません、、、。 (本来であれば、円グラフの上に「進学先 割合」というタイトルと、ホバーしたときのラベルが〇〇%と表示されます。) 追加記述したjsは以下の通りです。 //空の配列を用意 let ratio = []; //先ほど取得したtotalの値が[60, 30, 6, 96]だが、合計値である96は不必要なのでlengthから-1する for (let i = 0; i < total.length - 1; i++) { //total[i] - totalのi番目、つまり0番目の場合は60を取得 //[total.length - 1] - 配列の最後の値である96を取得(全体の合計値) let calc = (total[i] / total[total.length - 1]) * 100; ratio.push(calc); } console.log(ratio); //実行結果[62.5, 31.25, 6.25] - 合計値に対する、各カテゴリの割合が格納 Chart.js内のdataに、作成したratio変数をセットしてあげて完成です。 data: ratio おわりに 公式サイトでは、設定するdataの値が静的に配列で記述されていますが、 そのdataに、自分で作成した変数をセットすれば様々な要件に対応できるかと思います。 ※Chart.jsの基本的な使い方については、下記のサイトを参考に致しました。 Chart.jsでグラフを描画してみた 参考サイト Chart.js公式サイト Tooltipsの設定値一覧 Chart.jsでグラフを描画してみた pushで配列に要素を追加する方法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Redux-sagaでAPIと通信中にLoading画面を表示する

初めに APIを実行している時、Loading画面を表示できたらアプリっぽくなりますね。 なのでLoadingコンポーネントを作ってそいつを表示してみましょう ReactNativeで実装します 画面実装 こちらの記事を参考に実装していきます。 少し長くて申し訳ないのですが上からコピペしていくだけで全然OKです。 sliceの修正 まずはどちらか片方のsliceを修正します。 stateを追加して、API実行中と実行終了時にフラグを返します。 userSlice.js import {createSlice} from '@reduxjs/toolkit'; const userInitialState = { userName: null, userEmail: null, img: null, // 新しいstateを定義、初期値はfalse isLoading: false, }; export const userSlice = createSlice({ name: 'user', initialState: userInitialState, reducers: { getInfo: state => { state.userName = 'Tanaka Taro'; state.userEmail = 'practice@example.com'; }, resetInfo: state => { state.userName = userInitialState.userName; state.userEmail = userInitialState.userEmail; }, // 画像取得開始時 getImgStart: state => { state.isLoading = true; }, // 画像取得完了時 getImgSuccess: (state, {payload}) => { state.isLoading = false; state.img = payload; }, }, }); export const { getInfo, resetInfo, getImgStart, getImgSuccess, } = userSlice.actions; getImageStartをした時にisLoadingをtrue getImageSuccessの時にisLoadingをfalseにしてますね。 これによりLoadingコンポーネントを出し分けします(trueなら表示、falseなら表示しない方向で進めます) sagaの名前を修正 getImgStartとgetImgSuccessにしたのでsagaの該当箇所を修正します userSaga.js import axios from 'axios'; import {put, call, takeLatest} from 'redux-saga/effects'; // 修正 import {getImgSuccess} from './userSlice'; function baseAxios() { const url = 'https://dog.ceo/api/breeds/image/random'; return axios.get(url); } function* callApi() { try { const res = yield call(baseAxios); // 修正 yield put(getImgSuccess(res)); } catch (error) { console.log(error); } } export const getDog = () => ({type: 'GET_DOG'}); export function* userSaga() { yield takeLatest('GET_DOG', callApi); } コンポーネントの作成 簡単なLoadingコンポーネントを作成します。 まずはsrc配下にcomponentディレクトリを作成しましょう さらにその中にLoading.jsとindex.jsを作成します Loading.jsに関してはこんな感じで実装しました コピペしてしまってOKです笑 Loading.js import React from 'react'; import {StyleSheet, View, ActivityIndicator} from 'react-native'; const Loading = () => { return ( <View style={styles.container}> <ActivityIndicator size="large" color="salmon" /> </View> ); }; export default Loading; const styles = StyleSheet.create({ container: { flex: 1, alignContent: 'center', justifyContent: 'center', }, }); index.jsではLoadingコンポーネントをexportするだけです (Loading以外にもコンポーネントが多くなった時のためにexport用のファイルを分けています) index.js import Loading from './Loading'; export {Loading}; 画面で表示してみる HomeScreen.js上でLoadingコンポーネントを表示してみます HomeScreen.js import React from 'react'; import {StyleSheet, View, Text, Button, Image} from 'react-native'; // sliceからgetImgStartを新たにimport import {getInfo, resetInfo, getImgStart} from '../redux/user/userSlice'; import {getDog} from '../redux/user/userSaga'; import {useSelector, useDispatch} from 'react-redux'; import {Loading} from '../component/'; const HomeScreen = ({navigation}) => { const dispatch = useDispatch(); const user = useSelector(state => state.user); const dogImage = user.img?.data.message; const getImg = () => { // getImageStartでisLoadingをtrueにする dispatch(getImgStart()); // 画像取得完了時にisLoadingはfalseになる dispatch(getDog()); }; // isLoadingの値で画面を出し分ける return user.isLoading ? ( <Loading /> ) : ( <View style={styles.container}> <Text>私の名前は{user.userName}</Text> <Text>メールアドレスは{user.userEmail}</Text> <Image style={styles.img} source={{ uri: dogImage, }} /> <Button title="セットする" onPress={() => { dispatch(getInfo()); }} /> <Button title="リセットする" onPress={() => { dispatch(resetInfo()); }} /> <Button title="犬を取得" onPress={getImg} /> <Button title="画面遷移する" onPress={() => { navigation.navigate('Detail'); }} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, input: { width: '50%', borderWidth: 1, }, img: { width: '75%', height: '50%', }, }); export default HomeScreen; API通信中はこのようにLoading画面が表示されました。 画像データが取得されたらLoadingは消えて元の画面が表示されるはずです。 まとめ sliceにはAPI実行中、Loading画面を表示するためのstateを追加して各actionに処理を記載 Loadingコンポーネントを作成する 画面ではAPIを実行した時にLoadingコンポーネントが表示されるように記述を追加 簡単にですがこれでLoading画面が表示されるかと思います!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Chrome拡張"OneTab"から、ブックマークにエクスポートする

経緯、タブをoneTabに貯め過ぎて、整理が不可能に。 気づいたら1000個ぐらい。縦に長過ぎて整理が不可能。折り畳めないしマージもできない。 じゃあ、ブックマークに戻そう。がきっかけ。 oneTabに使えるエクスポート機能は無い まずChromeに取り込むには、特定形式のbookmarks.htmlにする必要ある。 Chromeからブックマークを書き出した時のファイル。 NETSCAPE-Bookmark-file-1形式? oneTabに簡易の書き出し機能はあるが、これは単純なURL改行のリスト。 そもそも日付情報が無い。 日付は重要なので捨てたくない。 まあ、便利なエクスポート機能あるとユーザー出て行きやすいから。 あえて付けなかったり(想像)、有料にしたりしてるのをよく見る。 表示部分からデータを解析する。 表示部分には日付データがちゃんと残ってる。 ターゲットはChrome拡張なので、Bookmarkletは使えない。 ctrl+shift+I で開発者ツールのconsoleからやる。 複数行のコードを弄るのは大変なので、ソース→スペニットを使って作る。 試行錯誤して・・・出来た。 この記事投稿してから気づいた大失態 oneTabのローカルストレージにJSONでデータが保存してあった! なので表示から解析する必要がなかった。 でもまあ、書き出したものは同じだし、面倒くさいからそのままでいいか。 スクリプトの使い方 下のコードをコピーして、ontab開いて、開発者ツールのコンソールに貼り付けて実行。 ただそういう攻撃もあるらしいので、軽くソースを読んでからのほうがいいらしい。 出来たhtmlファイルを、Chrome→ブックマークマネージャ→インポート スクリプトの主な機能 Chromeにインポートできる形式のhtmlを書き出す。 ブックマークにoneTabでの作成日を付与。 faviconも書き出す。 /* eslint no-undef: "error" */ //便利関数 var log = console.log , qsa = (s, o = document) => [...o.querySelectorAll(s)] , qs = (s, o = document) => o.querySelector(s) function saveTextToFile(fileName, text) { const blob = new Blob([text], {type: 'text/plan'}) const link = document.createElement('a') link.href = URL.createObjectURL(blob) link.download = fileName link.click() } //dataURL化 function getBase64(img) { var cvs = document.createElement('canvas') cvs.width = img.naturalWidth cvs.height = img.naturalHeight var ctx = cvs.getContext('2d') ctx.drawImage(img, 0, 0) //枠つけ // ctx.lineWidth = cvs.width / 3 // ctx.strokeStyle = '#0f0c' // ctx.strokeRect(0, 0, cvs.width, cvs.height) //canvas要素をBase64化する return cvs.toDataURL('image/png') } function oneTab2html() { //表示から解析してdataに入れる。 var data = qsa('.tabGroup').map((v) => { let dateText = qsa('div', v).find(v => v.innerHTML.startsWith('作成日時')).textContent.slice(5) let unixtime = new Date(dateText).getTime() / 1000 let obj = { tabs: null, title: (qs('.tabGroupTitleText', v).textContent.trim() + '_' || '') + dateText, time: unixtime, } obj.tabs = qsa('.tab', v).map(v => ({ url: qs('a', v).href, title: qs('a', v).textContent, favicon: (() => { let el = qs('img:nth-child(1)', v) if (el && el.src.startsWith('chrome://')) return getBase64(el) //''// return '' })(), })) return obj }) //dataからhtmlに書き出す var folders = '' data.forEach(v => { folders += ` <DT><H3 ADD_DATE="${v.time}" >${v.title}</H3>\n` folders += ' <DL>\n' v.tabs.forEach(a => folders += ` <DT><A HREF="${a.url}" ADD_DATE="${v.time}" ICON="${a.favicon}">${a.title}</A>\n` ) folders += ' </DL>\n' }) var html = `<!DOCTYPE NETSCAPE-Bookmark-file-1> <!-- This is an automatically generated file. It will be read and overwritten. DO NOT EDIT! --> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"> <TITLE>Bookmarks</TITLE> <H1>Bookmarks</H1> <DL><p> ${folders} </DL><p> ` //ファイルとして保存 saveTextToFile('OneTab→bookmarks.html', html) return true } oneTab2html() && '完了' インポート後、日付が書き出せてるか、確認する方法 素のChromeでは、ブックマークの作成日を確認する方法は見つからなかった。 なので以下の拡張機能を使いました。 自分用の雑記、試行錯誤のおおよそ時系列。 chromeから書き出したhtmlファイルを色々試す aのadd_date属性にUNIX時間を入れるらしい。 その他ブックマークのほうがネストが浅くて分かりやすい。 H3をフォルダと認識 フォルダにもadd_date時間を付けれる、LAST_MODIFIEDも ICON="data:image/png;base64, base64のiconデータも渡せる。 インポート インポート時はブックマークツールバーに入れられる ブックマークツールバーが空の時以外は、インポートしたブックマークフォルダが作られそこに収まる。 そのフォルダが存在する時は、同名のフォルダが作られる。 エクスポート時のフォルダ名が同じものは一つにマージされる、ブックマークは両方残る。 oneTabの表示から解析 googleとかoverflowとか一部のサイトはfaviconのsrcが無く、よくわからない。放置。 [{tabs:[{href,title,favicon},],time:UNIX時間,title:フォルダ名},] みたいな形式にまず変換。 出来上がったhtmlをctrl+s保存するために、window.open()を使う。 Chromeはブックマークのインポート形式がhtmlだから。 about:blankではctrl+sの保存できなかった。なぜぇ。 example.com開いて書き換えれば?→セキュリティー的に無理 もういいや、onetabの画面を直接書き換えてしまえ。 ctrl+sは可能だったが、 保存した瞬間ファイルが削除される怪奇現象。chrome-ext://だからぽい。 about:blankにhtml表示させて、そのouterHTMLをコピーしよう document.documentElement.outerHTMLをcopy()して、htmlに貼り付けた。 V1.html完成 V1.htmlインポートしみてる インポート失敗。まあそうか・・・。 インポートしてみるも数個の登録されるだけでおかしい。 Chromeが書き出したファイルと見比べると、Chromeはタグが大文字。!? Chrome書き出しファイルを小文字にしてインポートテスト→反応なし。 タグは大文字必須?! 大文字にしてからhtmlにparseしてからコピーすると、タグが小文字になってしまう。 タグと属性だけ大文字にするの・・・めんどくさいからparseするのやめよう。 html→保存、でなく直接書き出すことにした。 Chrome書き出しファイルを雛形にして書き出してコピー→htmlファイルに貼り付け。 V2.html完成 インポートするも、反映されない。 href←これも大文字必須だった。疲れた。もう全部大文字にしよう。 修正版を読み込ます→成功・・・日付がおかしい。 ブックマークの日付を調べるにはそれっぽい拡張が必要。 UNIX時間はミリ秒じゃなく秒だった。 インポート成功!! とりあえず機能的に完成。 ここまで来たら、Chromeが書き出したファイルで最小化を調べてる pタグを消してみる、問題なし、 フォルダ前のdl dt 削除、OK <h3>のフォルダより前は全部不要。文字コードはutf8なら削除できる。 <DL><DT>は改行無いと読み込まない→まじかあ。HTMLのこと詳しくないけど。 タイトルやH1は必要無い。 なんでこんな不親切な仕様なんだろう、超かしこ集団が作ってるブラウザなのに。 ADD_DATEはミリ秒じゃないく秒。10桁ぐらい。 <!DOCTYPE NETSCAPE-Bookmark-file-1>て検索すると情報が少し出てくる。 favicon取り込み(やらなくてもいい) ついでにfaviconに色つけてみようかな。 そしたら、リンク開く→アイコン更新で、visited的なことできそう。 そもそも更新されるのかな? とりあえず取り込もう canvas作ったにimg貼り付ける、naturalWidthを初めて知る。 base64化で、toData()→エラー クロスドメイン問題にひっかかった・・・別オリジンのimg貼り付けがあかんらしい。 ダメ元で.setAttribute("crossorigin","anonymous") 画像が壊れマーク。エラーは出なくなった。返り値は空に・・・。 DOMでスクリーンショット取れるとか聞いたことある、それはどうか? そもそもクロスドメインで全部弾かれてないんじゃないか? onetabオリジン→chrome-extension://burabura icon1→chrome://burabrua icon2→https://s2.googleusercontent.com ちゃんと調べるとhttp系だけが弾かれてる。じゃあこっち捨てよう。 完成、iconに色枠が付→見ると消えた。 この機能要らない。 気づいたこと ブックマークマネージャーだと同じfaviconは共通表示されてた。 ツールバーだと共通表示されない。初めて気づいた。 いままで後で読む、の区別にこの挙動を利用してた。 別のQiitaのBookmarkletの記事を読んでたら、偶然、ファイル保存の仕方を知る。 Qiitaへ投稿 onetabのローカルストレージにJSON発見。 個別のタブデータに日付情報は無し。 faviconもChromeURLでfavicon返すのに突っ込めばよさそう。 favicon返しに使えない例外の判定は作る必要ありそう。 bookmarks.htmlの最小構成を検証した結果 <H3 ADD_DATE="1617199649" LAST_MODIFIED="0">フォルダ1</H3> <DL> <DT><A HREF="https://news.google.com/" ADD_DATE="1606126600" ICON="data:image/png;base64,burabura=">ニュース</A> </DL> 手間取った点 Chrome開発者ツールのスペニットで作ってみた。 サジェスト効かないし、コーフォドーマットが汚すぎて読みづらい。 インポート形式の仕様がもっとゆるいと思い込み、試す→弾かれる、の繰り返しに疲れた。 最初から完コピを目指すべきだった。 軽い気持ちでワンライナーや、アロー関数オブジェクト返しを多様。 アイテムが多い上に例外多くて、そこでエラーでとまると、どこでエラーが起きたか分かりにくい。 console.assrtを使うべきかな。 テンプレートリテラルの中でforEachとかすると、凄く読みづらい。 join()ぐらいまでにすべき。 参考にさせてもらったページ ブックマークレット・コンソールを使って Webページ内の複数の指定要素のテキストをファイルに保存 - Qiita https://qiita.com/youtoy/items/7aaef1c44e0c77e0ced8
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Chrome拡張"OneTab"から、ブックマークにエクスポートする。

経緯、タブをoneTabに貯め過ぎて、整理が不可能に。 気づいたら1000個ぐらい。縦に長過ぎて整理が不可能。折り畳めないしマージもできない。 じゃあ、ブックマークに戻そう。がきっかけ。 oneTabに使えるエクスポート機能は無い まずChromeに取り込むには、特定形式のbookmarks.htmlにする必要ある。 Chromeからブックマーク書き出した時のファイル。 NETSCAPE-Bookmark-file-1形式? oneTabに簡易の書き出し機能は付いるが、これは単純なURL改行のリスト。 凄く使えない。 そもそも日付情報が無い。 日付は重要なので捨てたくない。 まあ、便利なエクスポート機能あるとユーザー出て行きやすいから。 あえて付けなかったり(想像)、有料にしたりしてるのをよく見る。 表示部分からデータを解析する。 表示部分には日付データがちゃんと残ってる。 ターゲットはChrome拡張なので、Bookmarkletは使えない。 ctrl+shift+I で開発者ツールのconsoleからやる。 複数行のコードを弄るのは大変なので、ソース→スペニットを使って作る。 試行錯誤して・・・出来た。 記事投稿してから気づいた大失態 oneTabのローカルストレージにJSONでデータが保存してあった! なので表示から解析する必要がなかった。 でもまあ、ちゃんと動くし、面倒くさいからそのままでいいか。 スクリプトの使い方 下のコードをコピーして、ontab開いて、開発者ツールのコンソールに貼り付けて実行。 ただそういう攻撃もあるらしいので、軽くソースを読んでからのほうがいいらしい。 出来たhtmlファイルを、Chrome→ブックマークマネージャ→インポート /* eslint no-undef: "error" */ //便利関数 var log = console.log , qsa = (s, o = document) => [...o.querySelectorAll(s)] , qs = (s, o = document) => o.querySelector(s) function saveTextToFile(fileName, text) { const blob = new Blob([text], {type: 'text/plan'}) const link = document.createElement('a') link.href = URL.createObjectURL(blob) link.download = fileName link.click() } //dataURL化 function getBase64(img) { var cvs = document.createElement('canvas') cvs.width = img.naturalWidth cvs.height = img.naturalHeight var ctx = cvs.getContext('2d') ctx.drawImage(img, 0, 0) //枠つけ // ctx.lineWidth = cvs.width / 3 // ctx.strokeStyle = '#0f0c' // ctx.strokeRect(0, 0, cvs.width, cvs.height) //canvas要素をBase64化する return cvs.toDataURL('image/png') } function oneTab2html() { //表示から解析してdataに入れる。 var data = qsa('.tabGroup').map((v) => { let dateText = qsa('div', v).find(v => v.innerHTML.startsWith('作成日時')).textContent.slice(5) let unixtime = new Date(dateText).getTime() / 1000 let obj = { tabs: null, title: (qs('.tabGroupTitleText', v).textContent.trim() + '_' || '') + dateText, time: unixtime, } obj.tabs = qsa('.tab', v).map(v => ({ url: qs('a', v).href, title: qs('a', v).textContent, favicon: (() => { let el = qs('img:nth-child(1)', v) if (el && el.src.startsWith('chrome://')) return getBase64(el) //''// return '' })(), })) return obj }) //dataからhtmlに書き出す var folders = '' data.forEach(v => { folders += ` <DT><H3 ADD_DATE="${v.time}" >${v.title}</H3>\n` folders += ' <DL>\n' v.tabs.forEach(a => folders += ` <DT><A HREF="${a.url}" ADD_DATE="${v.time}" ICON="${a.favicon}">${a.title}</A>\n` ) folders += ' </DL>\n' }) var html = `<!DOCTYPE NETSCAPE-Bookmark-file-1> <!-- This is an automatically generated file. It will be read and overwritten. DO NOT EDIT! --> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"> <TITLE>Bookmarks</TITLE> <H1>Bookmarks</H1> <DL><p> ${folders} </DL><p> ` //ファイルとして保存 saveTextToFile('OneTab→bookmarks.html', html) return true } oneTab2html() && '完了' 試行錯誤、おおよそ時系列。 chromeから書き出したhtmlファイルを色々試す aのadd_date属性にUNIX時間を入れるらしい。 その他ブックマークのほうがネストが浅くて分かりやすい。 H3をフォルダと認識 フォルダにもadd_date時間を付けれる、LAST_MODIFIEDも ICON="data:image/png;base64, base64のiconデータも渡せる。 インポート インポート時はブックマークツールバーに入れられる ブックマークツールバーが空の時以外は、インポートしたブックマークフォルダが作られそこに収まる。 そのフォルダが存在する時は、同名のフォルダが作られる。 エクスポート時のフォルダ名が同じものは一つにマージされる、ブックマークは両方残る。 oneTabの表示から解析 googleとかoverflowとか一部のサイトはfaviconのsrcが無く、よくわからない。放置。 [{tabs:[{href,title,favicon},],time:UNIX時間,title:フォルダ名},] みたいな形式にまず変換。 htmlでctrl+s保存するために、window.open()を使う。 Chromeはブックマークのインポート形式がhtmlだから。 about:blankではctrl+sの保存できなかった。なぜぇ。 example.com開いて書き換えれば?→セキュリティー的に無理 もういいや、onetabの画面を直接書き換えてしまえ。 保存した瞬間ファイルが削除される怪奇現象。chrome-ext://だからぽい。 about:blankにparseして、そのouterHTMLをコピーしよう document.documentElement.outerHTMLをcopy()して、htmlに貼り付けた。 V1.html完成 V1.htmlインポートしみてる インポート失敗。まあそうか・・・。 インポートしてみるも数個の登録されるだけでおかしい。 Chromeが書き出したファイルと見比べると、Chromeはタグが大文字。!? Chrome書き出しファイルを小文字にしてインポートテスト→反応なし。 タグは大文字必須?! 大文字にしてからhtmlにparseしてからコピーすると、タグが小文字になってしまう。 タグと属性だけ大文字にするの・・・めんどくさいからparseするのやめよう。 html→保存、でなく直接書き出すことにした。 Chrome書き出しファイルを雛形にして書き出してコピー→htmlファイルに貼り付け。 V2.html完成 インポートするも、反映されない。 href←これも大文字必須だった。疲れた。もう全部大文字にしよう。 修正版を読み込ます→成功・・・日付がおかしい。 ブックマークの日付を調べるにはそれっぽい拡張が必要。 UNIX時間はミリ秒じゃなく秒だった。 インポート成功!! とりあえず機能的に完成。 ここまで来たら、Chromeが書き出したファイルで最小化を調べてる pタグを消してみる、問題なし、 フォルダ前のdl dt 削除、OK <h3>のフォルダより前は全部不要。文字コードはutf8なら削除できる。 <DL><DT>は改行無いと読み込まない→まじかあ。HTMLのこと詳しくないけど。 タイトルやH1は必要無い。 なんでこんな不親切な仕様なんだろう、超かしこ集団が作ってるブラウザなのに。 ADD_DATEはミリ秒じゃないく秒。10桁ぐらい。 <!DOCTYPE NETSCAPE-Bookmark-file-1>て検索すると情報が少し出てくる。 favicon取り込み(やらなくてもいい) ついでにfaviconに色つけてみようかな。 そしたら、リンク開く→アイコン更新で、visited的なことできそう。 そもそも更新されるのかな? とりあえず取り込もう canvas作ったにimg貼り付ける、naturalWidthを初めて知る。 base64化で、toData()→エラー クロスドメイン問題にひっかかった・・・別オリジンのimg貼り付けがあかんらしい。 ダメ元で.setAttribute("crossorigin","anonymous") 画像が壊れマーク。エラーは出なくなった。返り値は空に・・・。 DOMでスクリーンショット取れるとか聞いたことある、それはどうか? そもそもクロスドメインで全部弾かれてないんじゃないか? onetabオリジン→chrome-extension://burabura icon1→chrome://burabrua icon2→https://s2.googleusercontent.com ちゃんと調べるとhttp系だけが弾かれてる。じゃあこっち捨てよう。 完成、iconに色枠が付→見ると消えた。 この機能要らない。 気づいたこと ブックマークマネージャーだと同じfaviconは共通表示されてた。 ツールバーだと共通表示されない。初めて気づいた。 いままで後で読む、の区別にこの挙動を利用してた。 別のQiitaのBookmarkletの記事を読んでたら、偶然、ファイル保存の仕方を知る。 Qiitaへ投稿 onetabのローカルストレージにJSON発見。 bookmarks.htmlの最小構成を検証した結果 <H3 ADD_DATE="1617199649" LAST_MODIFIED="0">フォルダ1</H3> <DL> <DT><A HREF="https://news.google.com/" ADD_DATE="1606126600" ICON="data:image/png;base64,burabura=">ニュース</A> </DL> 手間取った点 Chrome開発者ツールのスペニットで作ってみた。 サジェスト効かないし、コーフォドーマットが汚すぎて読みづらい。 インポート形式の仕様がもっとゆるいと思い込み、試す→弾かれる、の繰り返しに疲れた。 最初から完コピを目指すべきだった。 軽い気持ちでワンライナーや、アロー関数オブジェクト返しを多様。 アイテムが多い上に例外多くて、そこでエラーでとまると、どこでエラーが起きたか分かりにくい。 console.assrtを使うべきかな。 テンプレートリテラルの中でforEachとかすると、凄く読みづらい。 join()ぐらいまでにすべき。 参考にさせてもらったページ ブックマークレット・コンソールを使って Webページ内の複数の指定要素のテキストをファイルに保存 - Qiita https://qiita.com/youtoy/items/7aaef1c44e0c77e0ced8
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsを用いたクラス付与

Vue.jsを使って、スタイルの適用の仕方を学んだのでまとめておきます。 <div id="app"> <p v-bind:class="{large: fontLarge, org: color}"> Hello Vue.js </p> <button v-on:click="click"> クリック </button> <button v-on:click="large"> ラージ </button> <h2 v-bind:class="color? aqua : ''">ようこそ</h2> </div> var app = new Vue({ el: '#app', data: { fontLarge: false, color: false, aqua: { 'aqua': true, 'bg-black': true, }, hidden: false }, methods: { click: function() { this.color = true; this.hidden = true; }, large: function() { this.fontLarge = true; } } }) v-bind:class="{large: fontLarge, org: color}" 上記の記述を用いて、trueの時クラスを付与する記述としています。 その後のbuttonのクリックによるイベント発火で、クラスを付与できる仕様にしています。 v-bind:class="hidden? 'aqua' : 'hidden'">ようこそ</h2> こちらの記述でhiddenがtrueか否かでクラスを変える三項演算子です。 ボタンクリックでクラスを切り替えます。 スタイルの適用を切り替える方法について記述しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ウェブサイトに決済システムを簡単に組み込む方法 with Stripe【Stripe、Stripe Checkout、TypeScript, Javascript】

概要 本記事ではStripe Checkoutを使用してクライアントサイドだけで実装できる1回限りの購入の実装方法について解説します。サーバーサイドの開発が不要なので、開発工数がかからず簡単にウェブサイトに支払いシステムを組み込むことができます。 Stripe Checkoutとは Stripe Checkout はコンバージョンのために最適化された、Stripe がオンラインで提供する構築済みの支払いページです。1 回限りの購入でも、定期支払いでも、Checkout を使って簡単かつ確実にオンラインで支払いを受けることができます。 Stripe Checkout デモページ Stripe Checkout Docs Stripe Checkoutの流れ ウェブサイト内で支払いボタンを押す Stripe Checkout Pageにリダイレクト 必要なデータを入力し、決済を実行 決済の支払いステータスに合ったページにリダイレクト オーダー注文完了 実装方法 DashboardからCheckoutを有効化 サイトで販売する商品を追加(ProductとPriceを作成) 商品作成画面で諸々情報を追加する、※ One Time(一括)を選択すること 作成した商品のメニューボタンからcode snippetを選択 成功・キャンセル用のURLを指定する(会社のロゴをクリックするとキャンセル画面に飛ぶ仕様) Checkout code snippetをコピー HTMLに貼り付ける 請求先の入力を求める場合は、billingAddressCollectionをrequireにします。lineItemsのpriceにはダッシュボードまたはAPI経由で作成したpriceオブジェクトIDを入れます。 <!-- Load Stripe.js on your website. --> <script src="https://js.stripe.com/v3"></script> <!-- Create a button that your customers click to complete their purchase. Customize the styling to suit your branding. --> <button style="background-color:#6772E5;color:#FFF;padding:8px 12px;border:0;border-radius:4px;font-size:1em" id="checkout-button-price_1IffEvF3y14PGeyDugp6l5Z2" role="link" type="button" > Checkout </button> <div id="error-message"></div> <script> (function() { var stripe = Stripe('pk_test_........ID'); var checkoutButton = document.getElementById('checkout-button-priceID'); checkoutButton.addEventListener('click', function () { stripe.redirectToCheckout({ lineItems: [{price: 'price_ID', quantity: 1}], mode: 'payment', billingAddressCollection: 'required', shippingAddressCollection: { allowedCountries: ['JP'], }, submitType: 'pay', successUrl: 'https://yourdomain/success', cancelUrl: 'https://yourdomain/canceled', }).then(function (result) { if (result.error) { var displayError = document.getElementById('error-message'); displayError.textContent = result.error.message; } }); }); })(); </script> for more details: Stripe Checkout + サーバーサイドの開発 Checkout のクライアント側のみの組み込みでは、Stripe 上の商品を使用してチェックアウトページを生成します。動的な明細と金額が必要な場合は、Checkout クライアントおよびサーバ組み込みを使用する必要があります。 Webhook Webhookをセットすることで顧客情報やセッション情報など様々なデータを取得することが可能になります。Zapierでいろいろとできそうですが、Zapierでやりたいことができなければサーバーサイドの開発をするで良いと思いました。 サードパーティー連携 Zapier連携でStripeのデータを保存することも可能。 今回はクライアントオンリーなのでサーバーサイドの実装の説明を割愛しますが、少しだけ載せておきます↓ サーバーサイドでイベントデータを取得 checkout.session.complete を追加して、eventオブジェクトからデータを取得することができます。 const stripe = require('stripe')('sk_test_test'); const endpointSecret = 'whsec_...'; const app = require('express')(); const bodyParser = require('body-parser'); const fulfillOrder = (session) => { console.log("Fulfilling order", session); } app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => { const payload = request.body; const sig = request.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(payload, sig, endpointSecret); } catch (err) { return response.status(400).send(`Webhook Error: ${err.message}`); } if (event.type === 'checkout.session.completed') { const session = event.data.object; // ここに処理を追加 } response.status(200); }); app.listen(4242, () => console.log('Running on port 4242')); event.object.sessionから取得できるデータは以下のオブジェクトです。 { "id": "cs_test_aHs2hakj9ns4PFLEyb8yqKEWmD88vAU1IEjH9y6j6zGaBpH7O9cBwvds", "object": "checkout.session", "allow_promotion_codes": null, "amount_subtotal": null, "amount_total": null, "billing_address_collection": null, "cancel_url": "https://example.com/cancel", "client_reference_id": null, "currency": null, "customer": null, "customer_details": null, "customer_email": null, "livemode": false, "locale": null, "metadata": {}, "mode": "payment", "payment_intent": "pi_1Fo7c1F3y14PGeyDbcR4IxL5", "payment_method_options": {}, "payment_method_types": [ "card" ], "payment_status": "unpaid", "setup_intent": null, "shipping": null, "shipping_address_collection": null, "submit_type": null, "subscription": null, "success_url": "https://example.com/success", "total_details": null } まとめ 以上、サーバーサイドの開発なしで決済システムをウェブサイトに組み込むことができました。Stripe Checkout、素晴らしい。ウェブサイト上に決済システムを導入したいがフルで実装するのはお金と時間がかかりますが、Stripe Checkoutならさくっと実装できるので、サイト上で商品販売を行いたい方にとって非常に優秀なツールだなと感じました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

短期的なイベントハンドラー(短命なイベントハンドラー)は5秒未満

原文で言うa short-lived event handlerの定義が不明だった。 JavaScriptによるクリップボード操作にはユーザーの許可を得ずに実行できるパターンがある。 それがユーザー生成の短期的なイベントハンドラー内での実行であるが、 ユーザー生成の短期的なイベントハンドラーでないときにパーミッションを取らずに実行するとFirefoxの場合下記の警告が出力される。(コピーは失敗する) document.execCommand(‘cut’/‘copy’) はユーザー生成の短期的なイベントハンドラーの内部からの呼び出しでないため拒否されました。 今回は一般的に短期的なイベントハンドラーとして紹介されるclickイベント内で警告が出た。 今までの実績ある実装との違いは、 async functionである awaitを使っている fetchを使っている (fech処理の関係上)クリックからexecCommand実行まで時間が空く であった。 順番にテストした結果、およそ5秒を過ぎるとclickイベント内でも短命とみなされず失敗するという結果が得られた。 察するに、 ユーザー操作に対する短命なイベントハンドラー(例えば click ハンドラー) の「例えば click ハンドラー」は「ユーザー操作に対する(イベントハンドラー)」にかかっており、「短命」かどうかはユーザー操作関係なく、コード量にも関係なく、字面どおりに「短時間」であることが必要かと思われる。 私は「ユーザー操作に対するイベントハンドラー」≒clickイベント自体が「短命なイベントハンドラー」だと解釈していた。 コード <!DOCTYPE html> <html> <head> <title></title> </head> <body> <button id="test">copy</button> </body> <script type="text/javascript"> function sleep (ms) { return new Promise((res) => { setTimeout(res, ms)}) } document.querySelector('#test').addEventListener('click',async () => { await sleep(5 * 1000) document.execCommand('copy') console.log('copy') }) </script> </html> await sleep(5 * 1000)では失敗 await sleep(4 * 1000)では成功 await sleep(4800)あたりから成功 参考 クリップボードとのやりとり - Mozilla | MDN document.execCommand をユーザー操作に対する短命なイベントハンドラー(例えば click ハンドラー)のなかで実行することで、特別な許可なしに"切り取り"や"コピー"などのクリップボード操作が可能になります。 Interact with the clipboard - Mozilla | MDN The document.execCommand() method's "cut" and "copy" commands can be used to replace the clipboard's current contents with the selected material. These commands can be used without any special permission if you are using them in a short-lived event handler for a user action (for example, a click handler). How to copy a large amount of html content to clipboard in javascript without timeout - Stack Overflow In my browser the countdown stops after around 5 seconds. If I press Ctrl+C after the chain of copy handlers was broken, the copy handler is invoked again by a user generated event then it goes for 5 seconds again. 任意の文字列をコピーするテクニック - Qiita
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bootstrap4 Datepickerで期間の設定と取得

はじめに bootstrap-datepickerで「いつから」〜「いつまで」の期間を設定して、設定後に開始日付と終了日付を取得するサンプルを作ります。 オプションで日本語化の設定をして、日付の表示フォーマットをyyyy/mm/dddd形式に変更します。 See the Pen bootstrap-datepicker by saka212 (@saka212) on CodePen. Bootstrapの準備 最初にBootstrapで必要になるCSSとJavaScriptをhtmlに読み込みます。 CSSの指定 html <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> htmlのheadタグ内に記述します。 JavaScriptの指定 html <!-- jQuery, popper.js, Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> htmlファイル内のbodyタグ終了の前に記述します。(</body>の前に記述) bootstrap-datepickerを使うために、jQueryとpopper.jsも必要になるので、一緒に読み込んでおきましょう。 datepickerの準備 bootstrap-datepickerの本体を読み込む html <!-- bootstrap-datepicker --> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css"> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.ja.min.js"></script> Bootstrap用に読み込んだJavaScriptの下に続けて記述します。 日本語化で必要になるbootstrap-datepicker.ja.min.jsも一緒に読み込みます。 日付と期間選択用のhtml html <div class="input-daterange input-group" id="datepicker"> <div class="input-group-prepend"> <span class="input-group-text">開始日付</span> </div> <input type="text" class="input-sm form-control" name="from"> <div class="input-group-append"> <span class="input-group-text">終了日付</span> </div> <input type="text" class="input-sm form-control" name="to"> </div> ここでのポイントは、「開始日付」と「終了日付」を囲っているdivタグのclassにinput-daterangeを指定することです。 「開始日付」と「終了日付」の日付を取得する準備として、inputのname属性にfromとtoを指定しておきます。 datepickerを動かす JavaScript <script> // bootstrap-datepickerの設定 $('.input-daterange').datepicker(); </script> bootstrap-datepicker本体の読み込みの後に記述します。 日付と期間選択用のhtmlの作成で指定したclass名(.input-daterange)を設定。 以上で、datepickerを使った「開始日付」と「終了日付」の指定が出来るようになります。 オプション JavaScript <script> $('.input-daterange').datepicker({ // オプションを設定 language:'ja', // 日本語化 format: 'yyyy/mm/dd', // 日付表示をyyyy/mm/ddにフォーマット }); </script> 日本語化と日付表示のフォーマットは、bootstrap-datepickerに用意されているオプションを設定するだけで変更できます。 日本語化 オプションでlanguage:'ja'を設定。 (あらかじめbootstrap-datepicker.ja.min.jsを読み込んでおく必要があります) 日付フォーマット変更(yyyy/mm/dd) オプションでformat: 'yyyy/mm/dd'を設定。 選択した日付を取得 JavaScript <script> $('.input-daterange').datepicker({ language:'ja', format: 'yyyy/mm/dd', }) .on({ changeDate: function() { // datepickerの日付を取得 console.log('開始日付 :', $('input[name="from"]').val() ); // 開始日付を取得 console.log('終了日付 :', $('input[name="to"]').val() ); // 終了日付を取得 } }); </script> 選択した日付を取得するには、.on()に続けて上記のように処理を記述すればOKです。 日付と期間選択用のhtml作成時に用意した、name属性のfromとtoを利用して取得します。 完成サンプル 完成サンプル <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>bootstrap-datepicker</title> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> </head> <body> <div class="container"> <div class="row"> <div class="col-6"> <div class="input-daterange input-group" id="datepicker"> <div class="input-group-prepend"> <span class="input-group-text">開始日付</span> </div> <input type="text" class="input-sm form-control" name="from" /> <div class="input-group-append"> <span class="input-group-text">終了日付</span> </div> <input type="text" class="input-sm form-control" name="to" /> </div> </div> </div> </div> <!-- jQuery, popper.js, Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> <!-- bootstrap-datepicker --> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css"> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.ja.min.js"></script> <!-- bootstrap-datepickerの設定 --> <script> $('.input-daterange').datepicker({ language:'ja', // 日本語化 format: 'yyyy/mm/dd', // 日付表示をyyyy/mm/ddにフォーマット }) .on({ changeDate: function() { // datepickerの日付を取得 console.log('開始日付 :', $('input[name="from"]').val() ); // 開始日付を取得 console.log('終了日付 :', $('input[name="to"]').val() ); // 終了日付を取得 } }); </script> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bootstrap4 Datepickerで期間の設定と取得するサンプル

はじめに bootstrap-datepickerで「いつから」〜「いつまで」の期間を設定して、設定後に開始日付と終了日付を取得するサンプルを作ります。 オプションで日本語化の設定をして、日付の表示フォーマットをyyyy/mm/dddd形式に変更します。 See the Pen bootstrap-datepicker by saka212 (@saka212) on CodePen. Bootstrapの準備 最初にBootstrapで必要になるCSSとJavaScriptをhtmlに読み込みます。 CSSの指定 html <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> htmlのheadタグ内に記述します。 JavaScriptの指定 html <!-- jQuery, popper.js, Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> htmlファイル内のbodyタグ終了の前に記述します。(</body>の前に記述) bootstrap-datepickerを使うために、jQueryとpopper.jsも必要になるので、一緒に読み込んでおきましょう。 datepickerの準備 bootstrap-datepickerの本体を読み込む html <!-- bootstrap-datepicker --> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css"> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.ja.min.js"></script> Bootstrap用に読み込んだJavaScriptの下に続けて記述します。 日本語化で必要になるbootstrap-datepicker.ja.min.jsも一緒に読み込みます。 日付と期間選択用のhtml html <div class="input-daterange input-group" id="datepicker"> <div class="input-group-prepend"> <span class="input-group-text">開始日付</span> </div> <input type="text" class="input-sm form-control" name="from"> <div class="input-group-append"> <span class="input-group-text">終了日付</span> </div> <input type="text" class="input-sm form-control" name="to"> </div> ここでのポイントは、「開始日付」と「終了日付」を囲っているdivタグのclassにinput-daterangeを指定することです。 「開始日付」と「終了日付」の日付を取得する準備として、inputのname属性にfromとtoを指定しておきます。 datepickerを動かす JavaScript <script> // bootstrap-datepickerの設定 $('.input-daterange').datepicker(); </script> bootstrap-datepicker本体の読み込みの後に記述します。 日付と期間選択用のhtmlの作成で指定したclass名(.input-daterange)を設定。 以上で、datepickerを使った「開始日付」と「終了日付」の指定が出来るようになります。 オプション JavaScript <script> $('.input-daterange').datepicker({ // オプションを設定 language:'ja', // 日本語化 format: 'yyyy/mm/dd', // 日付表示をyyyy/mm/ddにフォーマット }); </script> 日本語化と日付表示のフォーマットは、bootstrap-datepickerに用意されているオプションを設定するだけで変更できます。 日本語化 オプションでlanguage:'ja'を設定。 (あらかじめbootstrap-datepicker.ja.min.jsを読み込んでおく必要があります) 日付フォーマット変更(yyyy/mm/dd) オプションでformat: 'yyyy/mm/dd'を設定。 選択した日付を取得 JavaScript <script> $('.input-daterange').datepicker({ language:'ja', format: 'yyyy/mm/dd', }) .on({ changeDate: function() { // datepickerの日付を取得 console.log('開始日付 :', $('input[name="from"]').val() ); // 開始日付を取得 console.log('終了日付 :', $('input[name="to"]').val() ); // 終了日付を取得 } }); </script> 選択した日付を取得するには、.on()に続けて上記のように処理を記述すればOKです。 日付と期間選択用のhtml作成時に用意した、name属性のfromとtoを利用して取得します。 完成サンプル 完成サンプル <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>bootstrap-datepicker</title> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> </head> <body> <div class="container"> <div class="row"> <div class="col-6"> <div class="input-daterange input-group" id="datepicker"> <div class="input-group-prepend"> <span class="input-group-text">開始日付</span> </div> <input type="text" class="input-sm form-control" name="from" /> <div class="input-group-append"> <span class="input-group-text">終了日付</span> </div> <input type="text" class="input-sm form-control" name="to" /> </div> </div> </div> </div> <!-- jQuery, popper.js, Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> <!-- bootstrap-datepicker --> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css"> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.ja.min.js"></script> <!-- bootstrap-datepickerの設定 --> <script> $('.input-daterange').datepicker({ language:'ja', // 日本語化 format: 'yyyy/mm/dd', // 日付表示をyyyy/mm/ddにフォーマット }) .on({ changeDate: function() { // datepickerの日付を取得 console.log('開始日付 :', $('input[name="from"]').val() ); // 開始日付を取得 console.log('終了日付 :', $('input[name="to"]').val() ); // 終了日付を取得 } }); </script> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bootstrap4 Datepickerで期間の設定と取得をするサンプル

はじめに bootstrap-datepickerで「いつから」〜「いつまで」の期間を設定して、設定後に開始日付と終了日付を取得するサンプルを作ります。 オプションで日本語化の設定をして、日付の表示フォーマットをyyyy/mm/dddd形式に変更します。 See the Pen bootstrap-datepicker by saka212 (@saka212) on CodePen. Bootstrapの準備 最初にBootstrapで必要になるCSSとJavaScriptをhtmlに読み込みます。 CSSの指定 html <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> htmlのheadタグ内に記述します。 JavaScriptの指定 html <!-- jQuery, popper.js, Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> htmlファイル内のbodyタグ終了の前に記述します。(</body>の前に記述) bootstrap-datepickerを使うために、jQueryとpopper.jsも必要になるので、一緒に読み込んでおきましょう。 datepickerの準備 bootstrap-datepickerの本体を読み込む html <!-- bootstrap-datepicker --> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css"> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.ja.min.js"></script> Bootstrap用に読み込んだJavaScriptの下に続けて記述します。 日本語化で必要になるbootstrap-datepicker.ja.min.jsも一緒に読み込みます。 日付と期間選択用のhtml html <div class="input-daterange input-group" id="datepicker"> <div class="input-group-prepend"> <span class="input-group-text">開始日付</span> </div> <input type="text" class="input-sm form-control" name="from"> <div class="input-group-append"> <span class="input-group-text">終了日付</span> </div> <input type="text" class="input-sm form-control" name="to"> </div> ここでのポイントは、「開始日付」と「終了日付」を囲っているdivタグのclassにinput-daterangeを指定することです。 「開始日付」と「終了日付」の日付を取得する準備として、inputのname属性にfromとtoを指定しておきます。 datepickerを動かす JavaScript <script> // bootstrap-datepickerの設定 $('.input-daterange').datepicker(); </script> bootstrap-datepicker本体の読み込みの後に記述します。 日付と期間選択用のhtmlの作成で指定したclass名(.input-daterange)を設定。 以上で、datepickerを使った「開始日付」と「終了日付」の指定が出来るようになります。 オプション JavaScript <script> $('.input-daterange').datepicker({ // オプションを設定 language:'ja', // 日本語化 format: 'yyyy/mm/dd', // 日付表示をyyyy/mm/ddにフォーマット }); </script> 日本語化と日付表示のフォーマットは、bootstrap-datepickerに用意されているオプションを設定するだけで変更できます。 日本語化 オプションでlanguage:'ja'を設定。 (あらかじめbootstrap-datepicker.ja.min.jsを読み込んでおく必要があります) 日付フォーマット変更(yyyy/mm/dd) オプションでformat: 'yyyy/mm/dd'を設定。 選択した日付を取得 JavaScript <script> $('.input-daterange').datepicker({ language:'ja', format: 'yyyy/mm/dd', }) .on({ changeDate: function() { // datepickerの日付を取得 console.log('開始日付 :', $('input[name="from"]').val() ); // 開始日付を取得 console.log('終了日付 :', $('input[name="to"]').val() ); // 終了日付を取得 } }); </script> 選択した日付を取得するには、.on()に続けて上記のように処理を記述すればOKです。 日付と期間選択用のhtml作成時に用意した、name属性のfromとtoを利用して取得します。 完成サンプル 完成サンプル <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>bootstrap-datepicker</title> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> </head> <body> <div class="container"> <div class="row"> <div class="col-6"> <div class="input-daterange input-group" id="datepicker"> <div class="input-group-prepend"> <span class="input-group-text">開始日付</span> </div> <input type="text" class="input-sm form-control" name="from" /> <div class="input-group-append"> <span class="input-group-text">終了日付</span> </div> <input type="text" class="input-sm form-control" name="to" /> </div> </div> </div> </div> <!-- jQuery, popper.js, Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> <!-- bootstrap-datepicker --> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css"> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.ja.min.js"></script> <!-- bootstrap-datepickerの設定 --> <script> $('.input-daterange').datepicker({ language:'ja', // 日本語化 format: 'yyyy/mm/dd', // 日付表示をyyyy/mm/ddにフォーマット }) .on({ changeDate: function() { // datepickerの日付を取得 console.log('開始日付 :', $('input[name="from"]').val() ); // 開始日付を取得 console.log('終了日付 :', $('input[name="to"]').val() ); // 終了日付を取得 } }); </script> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

モーダルで画像の拡大表示

デイトラWeb制作コース中級編DAY6の学び 【この記事に書いてあること】 プログラミング学習31日目⏩Web制作コース中級編DAY6?学習時間:1時間23分⏳学習内容:jQuery実践課題②✏学び?・モーダルで画像の拡大表示#デイトラ#プログラミング初心者#プログラミング学習 pic.twitter.com/Pz5v9TVsoW— ふりっく?トリプルゼロのWeb制作者 (@FuRiC_twi) April 13, 2021  【制作物】 【成果報告 PART7】モーダルで画像の拡大表示を実装しました?#プログラミング初心者#プログラミング学習#駆け出しエンジニアとつながりたい pic.twitter.com/0qId9rOmXF— ふりっく?トリプルゼロのWeb制作者 (@FuRiC_twi) April 13, 2021  【学び】 【HTML】 index.html <main class="course-wrapper"> <div class="course-item"> <img src="./img/web_first.png" alt="デイトラ初級コース"> <p>HTML / CSS / Bootstrap</p> </div> <div class="course-item"> <img src="./img/web_second.png" alt="デイトラ中級コース"> <p>HTML / CSS / JavaScript / jQuery</p> </div> <div class="course-item"> <img src="./img/web_third.png" alt="デイトラ上級コース"> <p>PHP / WordPress</p> </div> </main>      <div id="graydisplay"></div> 【CSS】 style.css #graydisplay { display: none; position: fixed; top: 0; left: 0; width: 100%; max-width: 100%!important; height: 100%; background: rgba(0,0,0,0.45); } #graydisplay img { position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-width: 60%; max-height: 60%; height: 60%; } 【JavaScript】 maiin.js $(function () { $("main img").click(function () { $("#graydisplay").html($(this).prop('outerHTML')); $("#graydisplay").fadeIn(200); }); $("#graydisplay, #graydisplay img").click(function () { $("#graydisplay").fadeOut(200); }); });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptの関数とメソッド

JavaScriptのメソッドと関数って何か違うん? と思っていたがちゃんと違ったので備忘録 ハマったこと class形式である処理を書いていた時に、addEventListerでメソッドを実行。 removeEventListerでメソッドを削除しよーと思ったら削除できなかった。 詳細 addEventListenerは無名関数は削除できないというのは知っていたので メソッドにスクロールで行いたい処理をまとめて、イベント付与! removeEventListerでもメソッド削除できると思っていたらできなかった HTML <button id="btn1">アラート出す</button> <button id="btn2">イベント削除</button> class Btn { constructor(){ this.alertBtn = document.getElementById('btn1') this.removeBtn = document.getElementById('btn2') this.addClickEvent() } addClickEvent() { // #btn1を押すとアラートが出る処理を付与 this.alertBtn.addEventListener('click', this.openAlert) // #btn2を押すと#btn1のアラートが出る処理を削除する処理を付与 this.removeBtn.addEventListener('click', this.removeEvent) } openAlert() { alert('click') } removeEvent() { #btn1のアラートが出る処理を削除 this.alertBtn.removeEventListener('click', this.openAlert) } } #btn1を押す アラートでる #btn2を押す #btn1を押す 4では再度アラートは出ないはずだったが実際は出てる 解決 メソッドはremoveEventListenerできないのでbindを使用 class Btn { constructor(){ this.alertBtn = document.getElementById('btn1') this.removeBtn = document.getElementById('btn2') this.addClickEvent() } addClickEvent() { // removeEventメソッドでも使用できるようにしておく this.alertBtnEvent = this.openAlert.bind(this) this.removeBtnEvent = this.removeEvent.bind(this) this.alertBtn.addEventListener('click', this.alertBtnEvent) this.removeBtn.addEventListener('click', this.removeBtnEvent) } openAlert() { alert('click') } removeEvent() { this.alertBtn.removeEventListener('click', this.alertBtnEvent) } } JavaScript難シイね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのclassでメソッドをremoveEventListerしたい

JavaScriptのメソッドと関数って何か違うん? と思っていたがちゃんと違ったので備忘録 ハマったこと class形式である処理を書いていた時に、addEventListerでメソッドを実行。 removeEventListerでメソッドを削除しよーと思ったら削除できなかった。 詳細 addEventListenerは無名関数は削除できないというのは知っていたので メソッドにスクロールで行いたい処理をまとめて、イベント付与! removeEventListerでもメソッド削除できると思っていたらできなかった HTML <button id="btn1">アラート出す</button> <button id="btn2">イベント削除</button> class Btn { constructor(){ this.alertBtn = document.getElementById('btn1') this.removeBtn = document.getElementById('btn2') this.addClickEvent() } addClickEvent() { // #btn1を押すとアラートが出る処理を付与 this.alertBtn.addEventListener('click', this.openAlert) // #btn2を押すと#btn1のアラートが出る処理を削除する処理を付与 this.removeBtn.addEventListener('click', this.removeEvent) } openAlert() { alert('click') } removeEvent() { #btn1のアラートが出る処理を削除 this.alertBtn.removeEventListener('click', this.openAlert) } } #btn1を押す アラートでる #btn2を押す #btn1を押す 4では再度アラートは出ないはずだったが実際は出てる 解決 メソッドはremoveEventListenerできないのでbindを使用 class Btn { constructor(){ this.alertBtn = document.getElementById('btn1') this.removeBtn = document.getElementById('btn2') this.addClickEvent() } addClickEvent() { // removeEventメソッドでも使用できるようにしておく this.alertBtnEvent = this.openAlert.bind(this) this.removeBtnEvent = this.removeEvent.bind(this) this.alertBtn.addEventListener('click', this.alertBtnEvent) this.removeBtn.addEventListener('click', this.removeBtnEvent) } openAlert() { alert('click') } removeEvent() { this.alertBtn.removeEventListener('click', this.alertBtnEvent) } } JavaScript難シイね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのclassでメソッドをremoveEventListenerしたい

JavaScriptのメソッドと関数って何か違うん? と思っていたがちゃんと違ったので備忘録 ハマったこと class形式である処理を書いていた時に、addEventListenerでメソッドを実行。 removeEventListenerでメソッドを削除しよーと思ったら削除できなかった。 詳細 addEventListenerは無名関数は削除できないというのは知っていたので メソッドにスクロールで行いたい処理をまとめて、イベント付与! removeEventListenerでもメソッド削除できると思っていたらできなかった HTML <button id="btn1">アラート出す</button> <button id="btn2">イベント削除</button> class Btn { constructor(){ this.alertBtn = document.getElementById('btn1') this.removeBtn = document.getElementById('btn2') this.addClickEvent() } addClickEvent() { // #btn1を押すとアラートが出る処理を付与 this.alertBtn.addEventListener('click', this.openAlert) // #btn2を押すと#btn1のアラートが出る処理を削除する処理を付与 this.removeBtn.addEventListener('click', this.removeEvent) } openAlert() { alert('click') } removeEvent() { #btn1のアラートが出る処理を削除 this.alertBtn.removeEventListener('click', this.openAlert) } } #btn1を押す アラートでる #btn2を押す #btn1を押す 4では再度アラートは出ないはずだったが実際は出てる 解決 メソッドはremoveEventListenerできないのでbindを使用 class Btn { constructor(){ this.alertBtn = document.getElementById('btn1') this.removeBtn = document.getElementById('btn2') this.addClickEvent() } addClickEvent() { // removeEventメソッドでも使用できるようにしておく this.alertBtnEvent = this.openAlert.bind(this) this.removeBtnEvent = this.removeEvent.bind(this) this.alertBtn.addEventListener('click', this.alertBtnEvent) this.removeBtn.addEventListener('click', this.removeBtnEvent) } openAlert() { alert('click') } removeEvent() { this.alertBtn.removeEventListener('click', this.alertBtnEvent) } } JavaScript難シイね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オフラインかつローカルファイル(file://)でTesseract.jsを使用する方法

概要 TesseractとはGoogleのOCRライブラリで、そのJavaScript版がTesseract.jsである。 Tesseract.js内では文字認識に用いる言語ファイルをHTTP経由でダウンロードしてくる処理があるため、デフォルトの設定ではオンライン状態じゃないと使用できない。 (2回目以降は言語ファイルのデータがindexedDBに保存されるためオフラインでも使用可) 予めローカルにDLした言語ファイルを参照するように設定もできるが、言語ファイルの読み込み処理がfetch APIで実装されてることと(ローカルファイル"file://"の読み込みはスキームサポート外)、ブラウザのセキュリティ設定によりローカルファイルの読み込みが制限されることから、オフラインで動作させるにしてもWebサーバー上であることが必須。 Webサーバーを用意しなくても、オフラインで動作するツールを作成してみた。 事前準備 ①以下資材をCDNからダウンロード  ※今回使用するTesseract.jsのバージョンは2.2.0 ・tesseract.min.js ・tesseract.min.js.map ・worker.min.js ・worker.min.js.map ・tesseract-core.wasm.js ②言語ファイルを以下URLからダウンロード  https://tessdata.projectnaptha.com/  「4.0.0-best」>「eng.traineddata.gz」を選択  ("xxx-best"版の方がOCR精度が高い) 日本語対応させたい場合は、「jpn.traineddata.gz」を使用する。 https://tessdata.projectnaptha.com/4.0.0_best/jpn.traineddata.gz しかし、Tesseract.jsの日本語の読み取り精度があまりよくないため、本稿では英語圏内の「eng.traineddata.gz」を使用する。 ③gzipの圧縮・展開ライブラリをダウンロード  https://github.com/imaya/zlib.js/blob/develop/bin/gunzip.min.js ツール作成 参考記事:https://qiita.com/hiroism/items/bd537e014e71d3cab1f9 OCR.html <html lang="ja"> <body> <input type="file" id="imagearea"> <br> <div> 進捗: <span id="progress">0</span>% </div> <div> <textarea id="result" style="height: 300px;width:300px;"></textarea> </div> <script src='js/tesseract.min.js'></script> <script src='js/gunzip.min.js'></script> <script> // 言語ファイルのデータをindexedDBに保存 writeCache(); document.getElementById('imagearea').addEventListener('change', read, false); function writeCache() { var req = indexedDB.open("keyval-store"); req.onupgradeneeded = function (event) { var db = event.target.result; db.createObjectStore("keyval"); var xhr = new XMLHttpRequest(); xhr.open('GET', 'traineddata/eng.traineddata.gz', true); xhr.responseType = 'arraybuffer'; xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { var arraybuffer = new Zlib.Gunzip(new Uint8Array(xhr.response)).decompress(); var transaction = db.transaction("keyval", "readwrite"); var keyvalStore= transaction.objectStore("keyval"); keyvalStore.add(arraybuffer,"./eng.traineddata"); } }; xhr.send(); } } function read(e) { Tesseract.recognize( e.target.files[0], 'eng', // 言語 { logger: m => { let progressArea = document.getElementById("progress"); progressArea.innerText = m.status + " " + Math.round(m.progress * 100); } , workerPath: 'js/worker.min.js', corePath: 'js/tesseract-core.wasm.js' } ).then(({ data: { text } }) => { // 結果の表示 const result = document.getElementById('result') result.value = text }) } </script> </body> </html> fetch対策のため、writeCache()関数でローカルの言語ファイルをXMLHttpRequestで読み込み、indexedDB(ブラウザのデータストレージ)に保存する処理を行っている。 以下、ディレクトリ構成 ツール実行 ブラウザのセキュリティ設定でローカルファイルの読み込みが制限されているため、以下起動オプションでGoogle Chromeを起動。 Chromeのプロセスを落とした後に実行 macOS /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --allow-file-access-from-files windowsの場合 参考記事:https://www.keyton-co.jp/blog/pc/windows/445/ OCR.htmlを立ち上げて「ファイル選択」を押下し、読み取り画像を選択する。 読み取り画像(Sample.png) 実行結果 追記:言語ファイルの読み込みについて Chrome拡張機能用のtesseract.jsを使用すれば、fetch対策を回避できるため、Tesseract.recognize関数で言語ファイルの格納場所を指定するだけでいけることがわかった。 Tesseract.recognize( e.target.files[0], 'eng', // 言語 { logger: m => { let progressArea = document.getElementById("progress"); progressArea.innerText = m.status + " " + Math.round(m.progress * 100); } , workerPath: 'js/worker.min.js', + langPath: 'traineddata', corePath: 'js/tesseract-core.wasm.js' } ・Chrome拡張機能用 https://github.com/jeromewu/tesseract.js-chrome-extension
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[js]replaceの書き方、正規表現

replaceのRegExpの書き方(アロー関数で短く各書き方)、正規表現のパターンいくつか(全部ではない)。 replace JavaScriptで検索して置き換えるreplaceの書き方。 new RegExpは正規表現と言われるやつで、今回の記事はnew RegExpでの書き方でより短かく書く書き方のメモ。もっと云うとfunction部をアロー関数で書いて全体的に短く書けるんだね、ということ。 *動的に正規表現を作成  『new RegExp()』を使うと配列や変数から正規表現を作成可
  *gフラグ:全文検索 該当するもの全部置き換え(gフラグがなかったら該当する最初の1コのみ)。 実際のコードで使うときgフラグ使ってることもあり、この記事ではgフラグも当たり前にある感じで話を進めます。 replace書き方:2種+1 (1) 通常の: replace( /△△/g, '〇〇'); 正規表現は「/ (スラッシュ)」で囲むので「/△△/g」。△△が置き換え前で、〇〇が置き換え後。△△や〇〇にはズバリ置き換え前の文字列を記述する。変数や配列は使えない。『え...、不便じゃね?』『配列や変数使えてナンボじゃねーの??』てことなので、replaceで配列や変数を使いたい場合はnew RegExp()使う。   (2) new RegExpの:function処理結果が置き換え後 replace( new RegExp(□□, 'g'), function(){return 〇〇; } );   (3) new RegExpでより短いの:function部をアロー関数で省スペース化 replace( new RegExp(□□, 'g'), ()=> 〇〇 ); 記号□□は配列とか変数。『RegExpのfunctionもアロー関数で書いちゃっていいのかな?動くのかな??』と相当の時間差で思い至った。動くなら1行あたりかなりの省スペース化具合...!!(緑色のヶ所)。   正規表現 全部ではなくいくつか。個人的に『???』なやつも入れてる(いつか分かる日が来るのか...) まずはいくつか例から。 例 ■コメントアウト行 (この例では「//」か「#」始まり行) /(^\s*\/\/.*\n|^\s*#.*\n)/g «解説» ・行の先頭(「^」)に半角スペースが0回以上あって(「\s*」) ・「//」or「#」 …or(「|」を書いて)以下任意で追加可 ・のあとに何らかの文字が0文字以上あり(「.*」) ・最後に改行コード(「\n」)がある «注意» この書き方で半角スペースや全角スペースを入れてはいけない (例えばhtmlなどのコードの感覚のまま)コードの視認性〜、とか何気なく入れたらNG インラインコードとコードブロックで書くとそれぞれ以下。 /(^\s*\/\/.*\n|^\s*#.*\n)/g /(^\s*\/\/.*\n|^\s*#.*\n)/g 例 ■その他 ・全角ひらがな ⇒ /[\u3040-\u309F]/g ・CJK統合漢字 ⇒ /\u3400-\u9FFF]/g ・CJK互換漢字 ⇒ /\uF900-\uFAFF]/g ・カタカナ ⇒ /\u30A1-\u30FA]/g ・大文字小文字英 ⇒ /[a-zA-Z]/g ・大文字小文字英+アンダーバーとハイフンも ⇒ /[a-zA-Z_-]/g ※「-(ハイフン)」を含ませたいときは最後に書く、じゃないと『[a-z]』の『aからz』の『から』のようなことになってうまく働かなくなる ■通常の文字として使う ・「\」正規表現の文字の特別な意味を除去 例) 「\.」で、なんらかの1文字の「.」としてではなく(後述)、単なる文字としての「.」ドット ■特別な意味を表す文字パターン ・「.」何らかの1文字 ・「\w」大文字小文字英、数字、「_(アンダースコア)」   「[a-zA-Z_0-9]」と同じ ・「\d」0〜9の数字 ※10進数 ※numberのnじゃないんかーい... 「[0-9]」 ・「\t」タブ文字 ・「\s」空白文字 ※文字コードがUTF-8の場合は全角スペースもマッチ、UTF-8じゃなかったら全角スペースはマッチしない   タブ、改行、改ページ、全角スペース、半角スペース「[\t\n\x0B\f\r]」 ・「\S」非空白文字 ※なんのこっちゃ ■繰り返しを表すパターン ・「*」0文字以上の繰り返し   「.*」何らかの1文字の0回以上繰返し   「/\d*/」0〜9の数字の0文字以上繰返し ・「+」1文字以上の繰り返し ※1文字以上存在しないと検出できない   「/\d+/」0〜9の数字の1文字以上繰返し ・「?」1回または0回 ・「{}」繰り返す回数を指定   「{,n}」 n回以下、「{n,m}」 n回以上m回未満 ■文字の位置を表すパターン ・「^」 行の先頭 ・「$」 行の末尾 ・「\b」 単語境界 ※なんのこっちゃよくわからん ・「\B」 単語境界以外 ※なんのこっちゃよくわからん ■任意の文字列を表すパターン ・「[ ]」一連の文字セットを表す ※1文字に限る ・「( )」文字パターンをグループ化する ※2文字以上のパターン ・「|」文字列を分ける ※2文字以上のパターンを複数列挙 ■先読み、先読み否定の2つ ・「txt1(?=txt2)」txt1に続いてtxt2が現れる場合のみ, txt1にマッチ ・「txt1(?!txt2)」txt1に続いてtxt2が現れない場合のみ, txt1にマッチ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CanvasOverlay (Leaflet 地図)

こんにちは。 Sumbera/L.CanvasOverlay.js というものを見つけました。canvas layer を作り(overlay)、その上に点などを自前で描画表示します。 これに少し手を加えて Leaflet 1.x で動くようにしました: kkdd/L.CanvasOverlay.js
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む