- 投稿日:2020-04-01T22:42:13+09:00
JavaScript JavaScriptでポップアップウィンドウを作成
完成品
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="style.css"> <script src="script.js"></script> </head> <body> <input type="button" id="btn" value="押す"> <!-- 変数に格納 --> <div id="box"> <!-- 変数に格納 --> <p id="close">× <!-- 変数に格納 --> <h2>ポップアップ</h2> </p> </div> </body> </html>CSS
@charset "UTF-8"; #box { background: #FFF; border: 1px solid #333; box-shadow: 0 10px 10px #999; display: none; /*! id要素に対してdisplay:none */ font-family: serif; padding: 10px; position: relative; text-align: center; width: 200px; } #box > #close { background-color: #EEE; color: #333; cursor: pointer; height: 30px; line-height: 30px; margin: 0; position: absolute; right: 1px; text-align: center; top: 1px; width: 30px; } #box > #close:hover { background-color: #F9F9F9; color: #999; } #btn { background-color: rgb(20, 114, 236); border: 0; color: #FFF; cursor: pointer; padding: 5px 20px; } #btn:hover { color: rgb(20, 114, 236); border: 1px solid rgb(20, 114, 236); background-color: rgb(255, 255, 255); } #btn:active { background-color: #4A4; }JavaScript
window.onload= function(){ //window の load イベントに対応するイベントハンドラ let box = document.querySelector("#box"); //id要素取得 let btn = document.querySelector("#btn"); //id要素取得 let close = document.querySelector("#close") //id要素取得 let boxstyle = box.style; //boxのstyle値をboxstyleに格納 btn.onclick = function(){ //btnがクリックされた時動かす関数 if(boxstyle.display === "block"){ boxstyle.display = "none"; }else{ boxstyle.display = "block"; } }; close.onclick= function(){ //closeがクリックされた時の関数 boxstyle.display = "none"; }; }
- 投稿日:2020-04-01T21:37:37+09:00
ngx-color-pickerで設定したカラーコードを取得する方法
Angular用のColorPickerのライブラリとして有名なものに ngx-color-picker というものがあります。
個人的に設定したカラーコードの取り出しに時間がかかったのでメモがてらここに残しておきます。
内容的には薄いので、寂しさを紛らわせるために導入の部分も日本語訳という大義名分のもと書きます。導入までの手順
インストール
npm install ngx-color-picker --saveモジュールの読み込み
import { ColorPickerModule } from 'ngx-color-picker'; @NgModule({ ... imports: [ ... ColorPickerModule ] })HTMLに組み込む
<input [(colorPicker)]="color" [style.background]="color"/>実際の表示はこんな感じ
めっちゃ便利ですね。
実際の利用方法としてtext入力欄の色がそのまま変化するのは少し寂しいので、こんな感じにカラーパレットのように利用するんじゃないかなと思います。
コードは以下のような感じです。
CSSは特に解説も無いのではしょります。<section> <label> <input type="radio"> <p class="color-pallet-custom" #ignoredInput [style.color]="color" [cpIgnoredElements]="[ignoredButton, ignoredInput]" [(cpToggle)]="toggle" [(cpPosition)]="top-right" [(colorPicker)]="color"></p> </label> <button #ignoredButton (click)="toggle=!toggle" class="btn-primary" type="button" ngbButtonLabel>色選択</button> </section>本題
ここまで設定できて『(゚∀゚)イイ!!』 ってなってたんですけど、どうやって値取り出すんだって結構試行錯誤しました。実際はこんな感じで取り出せます。
コード
<input type="radio" [value]="this.color">簡単ですね。
例えばreativeformを使って実装する際などはこのような形で値を渡してやることができます。
私がまだまだAngularに慣れていないからかわかりませんが、結構時間がかかってしまいました。
ngx-color-pickerは他にも便利なオプションがあるので、ぜひ参考にしてみてください。参考
ngx-color-picker
examples
How to Use Color Picker in Angular
- 投稿日:2020-04-01T17:42:39+09:00
HTML スクロール時に背景画像を遅らせる
後でコピペできるように残しておきます。
↓わかりにくいですが一応GIF
注意
スクロール時の背景画像の滑らかさはモニターのスペックに依存してしまうので、スペックの低いモニターだと結構カクツキます。
ゲーミングモニターだと滑らかに動くので良きですが...下記のコードではScrollイベントを使って背景画像のポジションを弄っているのですが、カクツキを完全になくすためならrequestAnimationFrameやsetIntervalを使わないとダメかも。
準備
背景に使用する画像を、
bg.png
という名前で準備しておきました。HTML
<div id="bg" class="ly_bg"></div>CSS
.ly_bg { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: -1; background-image: url('../images/bg.png'); background-size: 100% auto; background-repeat: repeat-y; background-position-y: 0; }JS
let bg = document.getElementById('bg') const rate = 3 window.addEventListener('scroll', function () { let y = window.scrollY bg.style.backgroundPositionY = `-${y / rate}px` })
- 投稿日:2020-04-01T17:22:16+09:00
Google Apps Scriptで快適にWEBスクレイピングするためのライブラリを作ったら、重すぎて動かなかったお話。
作るに至った経緯
GASでのWEBスクレイピングといえば、送られてきたhtmlソースをただの文字列として受け取り、正規表現やmatch、split、indexOfなどで無理矢理目的の要素・テキストを削り出してくるもの。一応スクレイピング用のライブラリもあるが、やっていることは対して変わらない。
「我々が求めるものはPythonのbs4のような、CSSセレクタなどで簡単に要素を見つけ出し、情報を抽出するものではなかろうか?」
そんなこんなで火が付いた。作ったプロトタイプもGoogle Chromeの開発者ツール上では、少々レスポンスが遅いもののキチンと想定道理に動いていたから、やれる自信はあった。再帰呼び出しの回数が多すぎて、どうやってもGASでは動かないと気付いた時、私は燃え尽きた。
どういうライブラリだったか
CSSセレクタベースで要素を見つけるもの。最初は親・子孫要素や属性(
[attr="hogehoge"]
)などにも対応させていたが、少しでも再帰呼び出しを減らすため、どんどんオプションを削られ、しまいには親要素からの検索も露と消えた。まあ結局動かなかったけど。というわけでこのライブラリ(笑)は、Google Chromeの開発者ツール上でしか、筆者は正常に動かしたことがない。悲しい。せっかく作ったのに。呼び出し
var h = HTMLparser(` <html> <head></head> <body> <p class="cls">htmlソーステキスト</p> ... </body> </html> `); var tag_p = h.search("p.cls"); //[{tagname: "p", attr: {…}, innerText: "htmlソーステキスト", tree: Array(1), parent: {…}}]呼び出し元のコード
function HTMLparser(html_str) { if(!(this instanceof HTMLparser)) { return new HTMLparser(html_str); }else { this.html = []; this.attr = {}; this.tags = {}; var html_obj = {tree: []}; //str: htmlソース, ary: htmlのツリー, parent: 親要素, self: HTMLparserのthisの参照用 function per(str, ary, parent, self) { var obj = {tagname: "_Node", attr: null, innerText: "", tree: [], parent: null}; obj.parent = parent; //開始タグを見つける var matchTag = str.match(/<([a-zA-Z][^\t\n\r\f \/>\x00]*?)(| [a-zA-Z][^\t\n\r\f>\x00]*?[^\/])>([\s\S]*?)$/); //[0]: 全体, [1]: タグ名, [2]: 属性, [3]: その開始タグ以降のテキスト function sameTagBothReg(tagname) { return new RegExp("(<" + tagname + ">|<" + tagname + " [a-zA-Z][^\t\n\r\f>\x00]*?[^\/]>|<\\/" + tagname + ">)"); } function sameTagStartReg(tagname) { return new RegExp("(<" + tagname + ">|<" + tagname + " [a-zA-Z][^\t\n\r\f>\x00]*?[^\/]>)"); } function sameTagEndReg(tagname, count) { return new RegExp("(<\\/" + tagname + ">)"); } var attrReg = /([a-zA-Z][^\t\n\r\f >\x00]*=\".*?\")/g; var attrReg_g = /([a-zA-Z][^\t\n\r\f >\x00]*)=\"(.*?)\"/g; if(matchTag) { var attr_obj = {}; var attr_node_list = matchTag[2].split(attrReg).filter(function(r) {return r.match(attrReg);}) attr_node_list.forEach(function(r) { //タグの属性を収集 var a = r.split(attrReg_g); if(!self.attr[a[1]]) { self.attr[a[1]] = {}; } var v; //クラスのみリスト化 if(a[1] == "class") { v = a[2].split(" "); v.forEach(function(r) { if(!self.attr[a[1]][r]) { self.attr[a[1]][r] = []; } self.attr[a[1]][r].push(obj); }); }else { v = a[2]; if(!self.attr[a[1]][v]) { self.attr[a[1]][v] = []; } self.attr[a[1]][v].push(obj); } attr_obj[a[1]] = v; }); obj.attr = attr_obj; obj.tagname = matchTag[1]; //その開始タグの対となる終了タグを見つける //もし開始タグがspanだった場合、その開始タグ以降のテキストから、 //spanの開始タグと終了タグをまとめて探索する var st_cnt = 1;//開始タグの数(開始タグはすでに一個あるので、初期値は1) var ed_cnt = 0;//終了タグの数 var sp_idx = 0;//目的の終了タグのインデックス var splitted_same_tag = matchTag[3].split(sameTagBothReg(matchTag[1])); splitted_same_tag.forEach(function(v, i) { if(sp_idx) { return; } //開始タグがマッチしたら+1 else if(v.match(sameTagStartReg(matchTag[1]))) { st_cnt++; return; } //終了タグがマッチしたら+1 else if(v.match(sameTagEndReg(matchTag[1]))) { ed_cnt++; //開始タグ数と終了タグ数が一致したら、インデックスを記録 if(st_cnt == ed_cnt) { sp_idx = i; }else { return; } } }); //始点からインデックスまでが、そのタグの子要素 var child = splitted_same_tag.slice(0, sp_idx).join(""); if(matchTag[1] == "title") { self.title = child; } //scriptタグの中には稀にhtmlのタグが紛れ込んでいるので、 //誤マッチ回避のため、scriptタグの中身は切り捨て if(matchTag[1] !== "script") { //子要素をターゲットにして自身(obj)を親とし、perを再帰的に呼び出し per(child, obj.tree, obj, self); } //インデックスから終点までが、そのタグの兄弟要素 var bro = splitted_same_tag.slice(sp_idx + 1).join(""); if(bro !== "") { //兄弟要素をターゲットにして自身と同じ親要素(parent) //を親とし、perを再帰的に呼び出し per(bro, ary, parent, self); } }else { obj.tree = [str]; } ary.unshift(obj); //もしタグ名が見つからなかったら(_Nodeのままなら)、 //自身と親・先祖のinnerTextに中身の文字列を追加 if(obj.tagname == "_Node") { Text(obj, obj.tree.join("")); function Text(o, txt) { o.innerText += txt; if(o.parent) { Text(o.parent, txt); } } }else { if(!self.tags[obj.tagname]) { self.tags[obj.tagname] = []; } self.tags[obj.tagname].push(obj); } } per(html_str.replace(/<!--[\s\S]*?-->/g, ""), this.html, null, this); } } //検索機能(最も削り取られた部分) HTMLparser.prototype.search = function(selector) { var sel = sel_parse(selector); //終端の1要素のみ読み取る //CSSセレクタは「右から」読み取るのが効率的 return check_tree(sel[0], this); function check_tree(s, self) { var r = []; var _tag = s.tag; if(_tag) { if(self.tags[_tag]) { for(var t of self.tags[_tag]) { var _atr = t.attr; if(s.class.length) { if(_atr.class) { var _chk = false; for(var cls of s.class) { if(_atr.class.indexOf(cls) < 0) { _chk = true; break; } } if(_chk) {continue;} }else { continue; } } if(s.id.length) { if(s.id[0] !== _atr.id) { continue; } } r.push(t); } } }else { var c_ary = []; var i_ary = []; var a_ary = [];//削り取られた残滓 if(s.class.length) { var _chk = true; var clsList = Object.keys(self.attr.class); for(var cls of s.class) { if(clsList.indexOf(cls) < 0) { break; }else { if(!c_ary.length) { c_ary = self.attr.class[cls]; }else { c_ary = c_ary.concat(self.attr.class[cls]).filter(function(x, i, self) { return self.indexOf(x) === i && i !== self.lastIndexOf(x); }); } } } } if(s.id.length) { var idList = Object.keys(self.attr.id); if(-1 < idList.indexOf(s.id[0])) { i_ary = self.attr.id[s.id[0]]; } } if(s.attr.length) { var _chk = false; for(var a of s.attr) { var _k = a.atr; var _m = false; if(_k.match(/(\*|\^|\$)$/)) { _m = _k.match(/(\*|\^|\$)$/)[1]; _k = _k.replace(/(\*|\^|\$)$/, ""); } } } var full_cnt = 0; if(c_ary.length) { full_cnt++; r = r.concat(c_ary); } if(i_ary.length) { full_cnt++; r = r.concat(i_ary); } if(a_ary.length) { full_cnt++; r = r.concat(a_ary); } if(1 < full_cnt) { r = r.filter(function(x, i, self) { return self.indexOf(x) === i && i !== self.lastIndexOf(x); }); } } return r; } //CSSセレクタの解析用 function sel_parse(sel) { if(sel.match(/ ?[\+\~] ?/g)) { throw Error('You cannot use Adjacent sibling combinator "+/~".'); } else if(sel.match(/\:(nth-child\(|nth-of-type\(|not\(|first-child|first-of-type|last-child|last-of-type)/g)) { throw Error('You cannot use Pseudo-elements like ":nth-of-type()"'); } var sp = sel.split(/( ?> ?|(?<=[a-zA-Z0-9\]\_\-]) (?=[a-zA-Z\[\.\#\_]))/g); var a = [], nxt = false; sp.forEach(function(s, idx) { if(s.match(/^ $/g)) { return; } else if(s.match(/^ ?> ?$/g)) { nxt = true; return; } var _o = {"tag": null, "class": [], "id": [], "attr": [], "next": false}; if(nxt) { _o.next = true; nxt = false; } var _s = s.split(/(\[.*?\]|(?<=(?:[a-zA-Z\]]|^))(?:\.|\#)[a-zA-Z\_][a-zA-Z0-9\_\-]*)/g).filter(function(r) {return r;}); _s.forEach(function(p) { if(p.match(/^\#/)) { _o.id.push(p.replace(/\#/, "")); } else if(p.match(/^\./)) { _o.class.push(p.replace(/\./, "")); } else if(p.match(/^[a-zA-Z]/)) { _o.tag = p; } else if(p.match(/^\[(.*?)(?:\=(?:\"(.*?)\"|\'(.*?)\')|)\]/)) { var _m = p.match(/^\[(.*?)(?:\=(?:\"(.*?)\"|\'(.*?)\')|)\]/); _o.attr.push({"atr": _m[1], "que": _m[2] ? _m[2] : ""}); } }); a.unshift(_o); }); return a; } }ちなみに、HTMLparserオブジェクトの中身は以下の通り。
treeにはその要素の子要素のリストが、parentにはその要素の親要素への参照が渡されている(図では一番外側のタグのため参照が無い)。下のattrとtagsは検索用の参照のリストが詰まっている。
- 投稿日:2020-04-01T13:20:30+09:00
横スクロールメニュー
ヌルヌルした横スクロールメニュー。
無限バージョンにしてみた。
メニューをあらかじめてんこ盛り用意しておいて、
疑似無限スクロールにする。ブラウザサイズによって表示が変わる。
また、 vuetouch.js を インストールしていることが条件。<style> .navigation-content { position: relative; max-width: 834px; min-width: 300px; width: 100%; -webkit-overflow-scrolling: touch; color: #b7b7b7 !important; } .navigation { display: flex; justify-content: space-between; align-items: center; width: 100%; list-style: none; overflow-x: auto; background: #fff; } .navigation a { display: inline-block; padding: 5px 15px; word-break: keep-all; text-decoration: none; border-bottom: 1px solid #e5e5e5; width:150px !important; background-color: #fff; } .navigation-selected { color: #666 !important; font-weight: bold !important; border-bottom: 1px solid black !important; } </style> <template> <div> <div class="navigation-content" style="margin: auto !important;"> <nav class="navigation" id="yokonavi"> <a :disabled="loading" @click="tabSelect(0)" v-bind:class="[ selectedTab === 0 ? 'navigation-selected' : 'elem-inactive' ]">ああイン順</a> <a :disabled="loading" @click="tabSelect(1)" v-bind:class="[ selectedTab === 1 ? 'navigation-selected' : 'elem-inactive' ]">いいイン順</a> <a :disabled="loading" @click="tabSelect(2)" v-bind:class="[ selectedTab === 2 ? 'navigation-selected' : 'elem-inactive' ]">ううイン順</a> <a :disabled="loading" @click="tabSelect(3)" v-bind:class="[ selectedTab === 3 ? 'navigation-selected' : 'elem-inactive' ]">ええイン順</a> <a :disabled="loading" @click="tabSelect(4)" v-bind:class="[ selectedTab === 4 ? 'navigation-selected' : 'elem-inactive' ]">おおイン順</a> <a :disabled="loading" @click="tabSelect(5)" v-bind:class="[ selectedTab === 5 ? 'navigation-selected' : 'elem-inactive' ]">ききイン順</a> <!--ブラウザのサイズによって、メニュー数を変更するか--> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(0)" v-bind:class="[ selectedTab === 0 ? 'navigation-selected' : 'elem-inactive' ]">ほげほげ順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(1)" v-bind:class="[ selectedTab === 1 ? 'navigation-selected' : 'elem-inactive' ]">いいイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(2)" v-bind:class="[ selectedTab === 2 ? 'navigation-selected' : 'elem-inactive' ]">ううイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(3)" v-bind:class="[ selectedTab === 3 ? 'navigation-selected' : 'elem-inactive' ]">ええイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(4)" v-bind:class="[ selectedTab === 4 ? 'navigation-selected' : 'elem-inactive' ]">おおイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(5)" v-bind:class="[ selectedTab === 5 ? 'navigation-selected' : 'elem-inactive' ]">ききイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(0)" v-bind:class="[ selectedTab === 0 ? 'navigation-selected' : 'elem-inactive' ]">んあイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(1)" v-bind:class="[ selectedTab === 1 ? 'navigation-selected' : 'elem-inactive' ]">んいイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(2)" v-bind:class="[ selectedTab === 2 ? 'navigation-selected' : 'elem-inactive' ]">んうイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(3)" v-bind:class="[ selectedTab === 3 ? 'navigation-selected' : 'elem-inactive' ]">んえイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(4)" v-bind:class="[ selectedTab === 4 ? 'navigation-selected' : 'elem-inactive' ]">んおイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(5)" v-bind:class="[ selectedTab === 5 ? 'navigation-selected' : 'elem-inactive' ]">んきイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(0)" v-bind:class="[ selectedTab === 0 ? 'navigation-selected' : 'elem-inactive' ]">ああイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(1)" v-bind:class="[ selectedTab === 1 ? 'navigation-selected' : 'elem-inactive' ]">いいイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(2)" v-bind:class="[ selectedTab === 2 ? 'navigation-selected' : 'elem-inactive' ]">ううイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(3)" v-bind:class="[ selectedTab === 3 ? 'navigation-selected' : 'elem-inactive' ]">ええイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(4)" v-bind:class="[ selectedTab === 4 ? 'navigation-selected' : 'elem-inactive' ]">おおイン順</a> <a :disabled="loading" v-bind:style="{ display: displayMode}" @click="tabSelect(5)" v-bind:class="[ selectedTab === 5 ? 'navigation-selected' : 'elem-inactive' ]">ききイン順</a> </nav> </div> <div v-touch:swipe.left="leftSwipe" v-touch:swipe.right="rightSwipe" style="height: 200px;background-color:red;"> ここをスワイプ </div> </div> </template> <script> import InfiniteLoading from 'vue-infinite-loading'; import {MixInfinit} from './MixInfinit' Vue.use(InfiniteLoading, { slots: { noMore: 'すべて読み込みました', // you can pass a string value noResults: '', // you can pass a string value }, }); export default { components: {InfiniteLoading}, mixins:[MixInfinit], data () { return { width: window.innerWidth, height: window.innerHeight, displayMode:'', data:{ x:0, y:0, px:0, py:0, width:0, harf_width:0, screen_width:0, scroll_x:0, scroll_now_x:0, yokonavi_width:0 }, selectedTab:1, loading:false, }; }, methods: { handleResize() { // resizeのたびにこいつが発火するので、ここでやりたいことをやる this.width = window.innerWidth; this.height = window.innerHeight; if(this.width > 834){ this.displayMode = 'none'; } else { this.displayMode = ''; } }, leftSwipe(){ // alert("左にスワイプされました"); if(this.selectedTab != 5){ this.selectedTab = this.selectedTab+1; } else { this.selectedTab = 0; } }, rightSwipe(){ // alert("右にスワイプされました"); if(this.selectedTab != 0){ this.selectedTab = this.selectedTab-1; } else { this.selectedTab = 5; } }, tabSelect(data){ this.selectedTab = data; } }, mounted() { window.addEventListener('resize', this.handleResize) }, beforeDestroy: function () { window.removeEventListener('resize', this.handleResize) }, created () { this.selectedTab = 0; this.handleResize(); }, watch: { selectedTab(newVal,oldVal) { var targetElement = document.getElementById("yokonavi"); var clientRect = targetElement.getBoundingClientRect() ; let seed = 444; let screen_harf_width = window.parent.screen.width/2; let minus_seed = screen_harf_width - 207; let waku_seed = this.selectedTab*100; targetElement.scrollLeft = seed-minus_seed+waku_seed; } }, } </script>コピペで動くよ。
- 投稿日:2020-04-01T08:04:54+09:00
position:relative・absoluteの使い方
- 投稿日:2020-04-01T05:19:14+09:00
初心者によるプログラミング学習ログ 278日目
100日チャレンジの278日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
278日目は、おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) March 31, 2020
278日目 1.0h
・xdデザインカンプからのサイト模写
・お問い合わせフォーム#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode