AWS Application Load Balancerでuser-agentを指定してアクセスを制限
先日業務で運用しているサーバーがAmazonbotというクローラーによる過剰なアクセスでサーバーに負担がかかっていました。 その際にAWS Application Load Balancerでuser-agentを指定してアクセスを制限したのでその方法を書き残したいと思います。
EC2 > ロードバランサー > アクセスを制限したいロードバランサーを指定 > Listenersで制限したいリスナーを指定 > Rules > Manage Rules > 以下のルールを挿入
ちなみにAmazonbotってなんだろうと調べてみると、
Amazonbotは、Amazonのウェブクローラーで、Alexaがお客様の質問にさらに多く回答できるようにするなど、サービス向上のために使用されています。Amazonbotは、標準的なrobots.txtのルールを尊重します。
とのことなのでアクセスを制限しても問題はないかと。
https://developer.amazon.com/ja/support/amazonbot
↓今回の障害に関してほぼ同様の事例が報告されていました。
2022年を振り返る!(筋肉8割エンジニア2割)
大晦日なので2022年を振り返りたいと思います。 ほぼ筋肉、少しエンジニアの話です。
1月-4月 減量
前年の12月から減量を開始しました。 前回、前々回と減量で得た自分なりの知見から比較的スムーズに減量は進んでいました。
減量が上手くいっていた要因
- 風呂やサウナ、メラトニンのサプリでちゃんと寝られた。
- 野菜を食べることで腸内環境がよかった。
- 空腹の度合いを見て炭水化物を多く取る日を作り、食事に上手く波をつけられていた。カーボリフィード気味になっていた思う。
のおかげでだったのかなと。
また今年から小沼さんのポージングアカデミーに参加するようになって最低限のポージングはできるようになったかなと思います。 やはりポージングは審査員をやるような方に見てもらうのが一番かなと。 あとフリーポーズも通っているゴールドのスタッフさんに見てもらっていました。
5月 大会
初戦東京ノービスは結果として75kg超級で8位と前年の4位から順位を落としフリーポーズも行えませんでした。
- ポージング
- 絞り
- 肩と腕
は良くなりましたが胸背中というか上半身全部が弱いですし、全体的にハリがなく萎んでいた印象でした。 帰りの電車では相当落ち込んで友達が応援に来ているのに結果が出ず情けなかったですし、ボディビル向いていないのかなとさえ思っていました。
ただ減量中薄々自分でも気づいていてはいました。このままのやり方ではダメだと。
そこから次のマッスルゲートまでの3週間では、
- 5分割だったのを3分割にする。
- 種目とセット数を限定する。無駄にボリュームを増やさない。
- 重量を伸ばしていく。
- 体重も少し増やす。
ことにしました。 結果2戦目では筋肉にハリが戻り東京ノービスで一つ上の順位だった人に勝ち、2位だった方とは審査表では良い勝負ができていました。 もちろん周りの方のコンディションが悪かったかもですが自分自身納得した身体で出場ができてとても満足でした。
マッスルゲート千葉大会結果
6月-8月 増量開始
正直この辺りは特に何もなく初戦以降のトレーニングのやり方を継続していました。 変わったところはこの辺りでトレ前にコンディショニングを今までより入念にやり始めました。おそらく30分くらいはしていたと思います。
9月-12月 パーソナルを受け始める
今通っているジムにパワーリフティングのトップ選手がいてその方とお話しできる機会がありそこからパーソナルを受け始めました。 元々パワーリフティングのトレーニングに興味があり、やはりナチュラルの選手は重量を追い求めることは必須だなと感じていました。
体のコンディショニングからスクワット、ベンチプレス、デッドリフトを中心に教わっています。 今もパーソナルを受け続けていすが、明らかに身体の使い方が変わりましたし重量も伸び怪我もほぼしなくなりました。
全体を振り返って
今年を振り返ってターニングポイントになったのは東京ノービスとパワーリフティングのトップ選手からパーソナルを受け始めたことかなと思います。
東京ノービスで負けたことで今までのやり方を変える良いきっかけになりました。 もし中途半端に3位とかになっていたら今までのとにかくボリューム、追い込むという考えが抜けきれていなかったと思います。 そう考えると負けてよかったと思いますし、そうでなかった時を考えると恐ろしいなと笑。 あと小沼さんや東京ノービスとパワーリフティングのトップ選手のようなちゃんとした人から教わるのは大事だなと痛感しました。 自分では気づけないことが多いですし師匠的な人を見つけるのは大事だと思います。
エンジニアに関して
今自分は会社で行っているプロジェクトでは開発周りはほぼ一人で作業をしています。(コーディングや少しjsなどを手伝ってくれる方はいますが) それゆえにバックエンドだけでなくインフラもフロントも全部携わることができ非常に貴重な経験をさせていただいています。 ただ他のエンジニアとの関わりが社外でも少なく、自分の狭い範囲で仕事をしている感じがあります。
3、4年前にSESからseb系の自社開発系に行きたいと思っていた頃は積極的にもくもく会に参加したり、時には運営側になったりと色々やっていました。
トレーニングでもですが他者と関わるといろんな影響を受けます。モチベーションをもらえたり自分が気づかなかったことに気づけたりなど。 そういうことが最近なかったなと反省しています。 あとアウトプットも全然できていなかったなと。
来年以降
筋肉
来年以降もBIG3やその他いくつかの種目の重量を伸ばすことを第一に考えてボリュームで補うことはしないようにトレーニングを行って行こうと思います。 大会の目標は特にないですが、東京ノービスで上位に入ってそろそろ卒業してクラス別に挑戦したいなとは思っています。 あとボディビル、トレーニングを必死にやることは良いですがそれで仕事やプライベートの楽しみなどが疎かにならないように上手く、長く付き合って行きたいなと。
エンジニア
筋肉よりこちらの方が課題かなと笑 とりあえず読んだ技術書や日々の仕事で疑問に思ったことはブログにまとめてアウトプットする癖をつけたいと思います。 あともくもく会とかで社外のエンジニアと関わる機会を増やしたいです。できれば筋肉エンジニアの会を小規模で良いからもう一回やりたいですね! あとできればトレーニング同様、師匠的な人を見つけられると良いかなと思っています。
プライベート
他にプライベートでやりたいこと
- 富士山に登る
- キャンプ
- ダイビング
- サーフィン
- 釣り
興味ある方誘ってください!!
railsでsvgにスタイルを当てる方法
svgファイルを読み込むhelperを作成する
# app/helpers/application_helper.rb def embedded_svg(filename, options={}) file = File.read(Rails.root.join('app', 'assets', 'images', filename)) doc = Nokogiri::HTML::DocumentFragment.parse file svg = doc.at_css 'svg' if options[:class].present? svg['class'] = options[:class] end doc.to_html.html_safe end
helperをviewに記述する
/ hoge.html.slim .svg_wrapper = embedded_svg('twitter_comment.svg', class: 'comment_svg')
すると以下のようなhtmlになる
<div class="svg_wrapper"><!--?xml version="1.0" encoding="utf-8"?--> <!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg class="comment_svg" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 512 512" style="width: 256px; height: 256px; opacity: 1;" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <style type="text/css"> </style> <g> <path class="st0" d="M339.602,46.658H172.398C77.173,46.658,0,123.849,0,219.048c0,95.225,77.173,172.416,172.398,172.416h122.568 c24.641,0,32.324,11.437,32.324,20.799c0,13.849-7.189,26.831-24.606,39.248c-10.793,7.684-2.314,13.831,6.147,13.831 C418.878,465.342,512,345.282,512,219.048C512,123.849,434.81,46.658,339.602,46.658z"></path> </g> </svg> </div>
cssを当てる
.svg_wrapper { height: 50rem; padding: 30rem; .comment_svg { width: 1rem !important; height: 1rem !important; &:hover { fill: red; } } }
ここで注意なのが、svgのhtmlを確認すると
style="width: 256px; height: 256px; opacity: 1;"
のようにスタイルが当たっている場合があるので、svgファイルのスタイルが優先されてしまう。
このような場合は、これらの記述を消すか、以下のように
width: 1rem !important; height: 1rem !important;
で優先する必要がある。
すると以下のようにsvgにスタイルを当てることができる。
レスポンシブ対応させる方法
画面幅が799px以下ならスマホ、800以上ならpcのスタイルを当てる方法
手順は以下
ブレイクポイントの記述
//app/assets/stylesheets/foundation/_variable.scss // 幅の設定 ----------------------- $sp-breakpoint: 799px; // ブレイクポイント ------------------- $breakpoints: ( sp: "max-width:#{$sp-breakpoint}", pc: "min-width:#{$sp-breakpoint + 1px}", );
ここでは$sp-breakpoint
という変数に799px
という値を入れ、
$breakpoints:
というマップ(scssのデータ型)にsp(スマホ)の場合とpcの場合のブレークポイントを入れている。
つまり中身は、
// ブレイクポイント ------------------- $breakpoints: ( sp: "max-width:799px", pc: "min-width:800px", );
となる。
mixinの記述
//app/assets/stylesheets/foundation/_mixin.scss @mixin sp { @media (map-get($breakpoints, 'sp')) { @content; } } @mixin pc { @media (map-get($breakpoints, 'pc')) { @content; } }
先ほどのマップ$breakpoints:
を取り出すためにmap-get
という関数を使う。
第一引数$breakpoints
というマップのキーである第二引数sp
と取り出している。
@media (max–width: 799px)
799px以下ではスマホ用cssを適用するためのメディアクエリとなる。
view port の記述
/ app/views/layouts/application.html.slim doctype html html head title | title / 以下を追記する↓ meta[name="viewport" content="width=device-width,initial-scale=1.0"] = csrf_meta_tags = csp_meta_tag = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' body
viewportとは、表示領域のこと。Webページの横幅がviewportとなる。
viewportを設定していないPC向けのページをスマートフォンで表示すると、文字もページも小さくなってしまう。
詳しくは以下
viewportを理解して正しいレスポンシブデザインを設定しよう | デジタルマーケティング・Web制作・PR支援のBigmac inc
実際にcssを当てる
.hogehoge{ width: 10rem; height: 10rem; background: red; @include sp { width: 5rem; height: 5rem; }
通常の場合は、width: 10rem;
,height: 10rem;
としたいけど、
spの場合はwidth: 5rem;
,height: 5rem;
という意味。
@include sp {}の部分が@contentの部分に挿入されることになる。
これを書けばsp、pcそれぞれにスタイルを当てることができる。
余談: レスポンシブをさせたい時にハマってしまったのが、
- view portの書き忘れ
- ブラウザの表示の倍率を変えていた
からだった。
自戒のためにこの記事を書きました。。。
筋肉エンジニアを名乗っていたニート時代を振り返る
今年2020年の3月から10月の間ニートだった。
社会人になってからこんなにまとまった時間を確保できるのは稀だと思う。
初期のニート時代の記憶はもう薄れてしまっているが、忘れないうちに貴重な?ニート期間に学んだ?ことを書き残したい。
最初は時系列的に、後半は細々したことを書き残す。
ニート初期:ニート概ねストレスはなくなるが、無職の罪悪感というストレスはある
前職はかなり忙しくて休日にも作業を進めないといけない状況だった。
大分記憶が薄れしまっているけど、休日にも"やらなければいけない"ことがあるというのは大きなストレスだった。
しかし、ニートになり、
いつでも好きなタイミングに、起きて、食べて、筋トレして、アニメ観て、寝る。
こんな幸せがあろうか!!いや、ないっっ!!!
という幸せを日々噛み締めニート初期時代を生きていた。
ただそのうち無職であるからこそ自分が、働いていない、社会に属していない、社会のお荷物、う◯こ製造機というストレスを抱えてしまうことになる。
抱える必要のないストレスを抱えたニートだが最初の頃は、ブログ更新、ポートフォリオ作成など勉強はしており、充実していた。(もちろん筋トレも)
ニート中期:ニート転職は難しい
当初、自分のニート計画では最初の1ヶ月Jsを勉強、その後数ヶ月ポートフォリオ作成、その後転職というくそ楽観的な未来を考えていた。
人生というのは自分の思い通りに進む事ばかりではない。
むしろうまくいかないことの方が多い。
特にこういう、
「まあ、なんとかなるっしょ!!」
という時が一番危なく、現実そうなってしまう。
そもそも自分の実力がない、面接が下手、応募の数が少ない、ということもあるが、転職は大変だったと感じた。
新卒でSIerで働き、2年目の5月に退職し約9ヶ月ほどwebでの経験はあった。
だけど、経験年数3年ないと応募できないところが多かった印象。
そんなところばかりではないけど、コロナ渦で、微妙な経験年数、1年前後で退職が2回というのが足かせになってたと思う。
(もちろん実力があるひとはすぐ決まると思うけどね)
反省でいうと、前職を辞めた直後から、もっと言えば、辞める前に転職活動を始めて転職先を決めておくべきだったなと。
普通はそうすると思うけど、自分はなぜかうまくいくと思っていた笑笑
ニート絶望期:ニート頑張っている人を見るのが嫌になる
転職が思うようにうまくいかず勉強のモチベが下がった頃、頑張っている人を見るのが嫌になってしまった。
特にツイッターでの話で、
自分とは反対に頑張っている人のツイートを見ると自分と比較してしまい嫌な気分になった。
なんで、俺は何もしていないんだろうと。
(もちろん筋トレはしている。むしろ筋トレで最低限の精神の安定を保っていた。)
ニート終末期:ニート無職でも楽しむ
勉強のモチベがほぼなくなっていた頃によく2ちゃんねる創設者のひろゆきの動画を見ていた。
特に天下一無職会という動画が面白かった。
無職の人の失敗談や楽しみなどの投稿を紹介するラジオ的なやつ。
最初、自分と同じように罪悪感を感じているニートばかりだと思ってたけど、そうではなかった。
無職だから、時間があるからこそ、それを有意義に使っている人がいた。
例えば、新しくギターを始めたり、拾った葉っぱをお茶にして飲んだりとか。
それら全部が羨ましいとは思うわけではないけど、
その時に、どんな状態でも幸せを感じることはできるし、逆にどんな良い状態でも気持ち次第で幸せと感じられないのかなと思った。
無職になってこれが一番気づけて良かったと思うことだった。
ニートの終わり:ニート転職す!
そんなこんなで、ありがたいお話があり転職を決めることができた。
転職を決められた要因は、
かなと。(あと筋肉)
実際の正規ルート(応募して面接して採用)ではなかったのだけど、
転職の話を頂いた時に、自分のやったことをいくつも送られたことは、良かったのかなと思う。
ここから先は、細々とした学びを羅列していく。
時間があるからといって勉強するわけではない
これはそう。
人間メリハリが大事。(おそらくそうだけど、主語がでかい)
どんなに時間があっても惰性でyoutubeやSNSを見てしまい時間を浪費する。
ではどうすれば勉強するのか。
それは、
- 今後必要なことか興味のあるものを勉強する
- ご褒美のために勉強する
かなと。(自分の場合はね)
- 今後必要なことか興味のあるものを勉強する
やらなくてはいけない、やりたい、の両方もしくはどちらかでないと手をつけることは永遠にない。
- ご褒美のために勉強する
これは意外に有効だった。
これを買うためにこの日までのこの作業を終わらせるといった感じでやると良かった。
期間を定めて集中できるし、達成できた時に、うぉぉおおおおやったぜぇえええええってなって脳内麻薬どっひゃぁあああああってなる笑
睡眠時間を確保することは心の健康に良い、あと筋肉にも良い
これはニートじゃなくてもできることだと思うし、やるべきことだと思う。
元から睡眠を削る方でないけど、前職では平日かなり時間がなくて7hくらいしか確保できなかった気がする。
(7hも寝てるじゃねーかと突っ込みたくなるかもしれないが、私は最低8h寝ないと日中眠くなってしまう。)
仮に私の睡眠時間が8h必要だったとして平日7hしか寝ていないと、平日5h分のツケを休日に支払うことになる。
事実、前職当時では休日昼過ぎまで寝ていた。
逆にニート時代と現在はしっかり睡眠時間を確保したおかげで、休日も概ね平日と同じように活動できている。
そして、筋肉にも良い影響が出ている気がする。
筋トレやり過ぎにご注意
平日の昼間に筋トレをする、これ以上の幸せがあろうか、いや、ないっっ!!
という幸せを日々噛み締め、毎日気付いたらジムで筋トレをしていた。
筋トレをめちゃくちゃやりこむ時期は必要だと思う。
最初のうちだったら、やったらやった分だけ結果は出るし、
初心者ボーナス期間が終わってても、量をこなす分トレーニングの新たな発見だったりトレーニング自体がうまくなる気がする。
ただ、やったらやった分だけ結果が出るのは前述したように初心者だけだと思う。(ナチュラルの場合は)
どういう理屈かというと、
初心者は神経系が発達していないため高重量を扱えずに筋肉に高負荷の刺激を入れられない。
なので、やり込んでも平気。
初心者を抜けると重量を扱えるようになるので、それをやり込みすぎてしまうと、回復に時間がかかる。
筋肉は、刺激を与え、栄養を入れ、回復して、より強い刺激を与え、成長していく。大きくなる。
やり込みすぎはこの回復がし切れないためにより強い刺激を与えることができず、結果、あまり成長しない。
ということが起こるのではないかと最近考えているし、最近の筋トレ界隈の風潮。
色々理論があるため、一概にこれが正しいとは言い切れないけど、何事も適切な量があると思う。
筋トレに関して、もっと書きたいことが出てきたので別の記事で書くことにする。
アニメはいいぞ
元々アニメはめちゃくちゃ観る方ではなかったけど、エンジニアになり周りで好きな人が多かったため、観るようになった。
ジャンルで言うと、可愛い女の子が出てくるものが多い。(自分で書いといてなんだけども草)
なので、これといって人生に役立つかと聞かれるとそれはないっと即断できる。(即断っだとっ//)
ただ、"面白いもの"に触れるというのは、人生の余暇としては良い時間の過ごし方だと思う。
それに、これは〇〇の役に立つから趣味にする!ってだけというのは個人的につまらない生き方かなと思う。
逆に、これ全く役に立たないんだけどめちゃくちゃ好きでついやっちゃうんだよね!っていう人の方が魅力的に映るし、そういう人の話を聞きたい。
ある意味"人間らしいな"って。
最後に
自分が感じたことでもいつか忘れてしまうので、書き残したいと思った。
ただ今回この文章を書いてみて、改めて気づくこともあったので今後も、ふとした時にブログを更新したいなと思う。
こんな拙い文章でも面白いなと感じた方がいらっしゃればTwitterでもフォローしといてください。
【js】DOM ノードを追加、置換、削除する
ノードの追加、置換、削除についてまとめます。
HTMLの編集には、innerHTMLプロパティを利用することもできますが、今回紹介する方法だと以下のようなメリットがあります。
- オブジェクトツリーとして操作できるので、対象となるコンテンツが複雑になった場合にも、コードの可読性が悪くなりにくい
- 要素、属性とテキストとを区別して扱えるので、ユーザーからの入力によってスクリプトが混入するような危険は回避しやすい
シンプルなコンテンツの編集 → innerHTMLプロパティ
複雑なコンテンツの編集 → 本節のアプローチ
ノードを追加
以下はフォームに入力した内容でリンクを追加するコードです。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>heading.html</title> </head> <body> <form> <div> <label for="name">サイト名:</label><br /> <input id="name" name="name" type="text" size="30" /> </div> <div> <label for="url">URL:</label><br /> <input id="url" name="url" type="text" size="50" /> </div> <div> <input id="btn" name="name" type="button" value="追加" /> </div> </form> <div id="list"></div> </body> <html>
document.addEventListener('DOMContentLoaded', () => { document.getElementById('btn').addEventListener('click', () => { // テキストボックスを取得 const name = document.getElementById('name'); const url = document.getElementById('url'); // <a>要素を生成 let link = document.createElement('a'); // <a>要素のhref属性を設定 link.href = url.value; // テキストノードを生成 const text = document.createTextNode(name.value); // テキストノードを<a>要素の直下に追加 link.appendChild(text); // <br>要素を生成 const br = document.createElement('br'); // <div id="list">を取得 let list = document.getElementById('list'); // <div>要素の直下に<a>,<br>要素の順番で追加 list.appendChild(link); list.appendChild(br); }, false); }, false);
1. 要素、テキストノードを作成する
コンテンツを追加するにはまず、createElement, createTextNodeメソッドを使用し、追加したい要素、テキストノードを生成します。
今回だと以下のような箇所で行われています。
// <a>要素を生成 let link = document.createElement('a'); // テキストノードを生成 const text = document.createTextNode(name.value); // <br>要素を生成 const br = document.createElement('br');
他にも生成するノードに応じて以下のようなメソッドがあります
// 要素ノード createElement(要素名) // 属性名ノード createAttribute(属性名) // テキストノード createTextNode(テキスト)
createHgehogeでノードを作成した時点では、お互いの階層関係を意識する必要はありません。
現状はバラバラのままなのでこの後の作業で組み立てる必要があります。
2. ノード同士を組み立てる
1の段階でバラバラになっているノードを組み立ててドキュメントに追加する必要があります。
この作業を行うのがappendChildメソッドです
appendChildメソッドは指定された要素を現在の要素の最後の子要素として追加します。
// テキストノードを<a>要素の直下に追加 link.appendChild(text); link.firstChild; // "qiita" // <div>要素の直下に<a>,<br>要素の順番で追加 list.appendChild(link); list.appendChild(br); list.children; //HTMLCollection(2) [a, br]length: 20: atarget: ""download: ""ping: ""rel: ""relList: DOMTokenList [value: ""]hreflang: ""type: ""referrerPolicy: ""text: "qiita"coords: ""charset: ""name: ""rev: ""shape: ""href: "https://qiita.com/"origin: "https://qiita.com"protocol: "https:"username: ""password: ""host: "qiita.com"hostname: "qiita.com"port: ""pathname: "/"search: ""hash: ""title: ""lang: ""translate: truedir: ""hidden: falseaccessKey: ""draggable: truespellcheck: trueautocapitalize: ""contentEditable: "inherit"isContentEditable: falseinputMode: ""offsetParent: bodyoffsetTop: 132offsetLeft: 8offsetWidth: 33offsetHeight: 16style: CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}innerText: "qiita"outerText: "qiita"oncopy: nulloncut: nullonpaste: nullonabort: nullonblur: nulloncancel: nulloncanplay: nulloncanplaythrough: nullonchange: nullonclick: nullonclose: nulloncontextmenu: nulloncuechange: nullondblclick: nullondrag: nullondragend: nullondragenter: nullondragleave: nullondragover: nullondragstart: nullondrop: nullondurationchange: nullonemptied: nullonended: nullonerror: nullonfocus: nullonformdata: nulloninput: nulloninvalid: nullonkeydown: nullonkeypress: nullonkeyup: nullonload: nullonloadeddata: nullonloadedmetadata: nullonloadstart: nullonmousedown: nullonmouseenter: nullonmouseleave: nullonmousemove: nullonmouseout: nullonmouseover: nullonmouseup: nullonmousewheel: nullonpause: nullonplay: nullonplaying: nullonprogress: nullonratechange: nullonreset: nullonresize: nullonscroll: nullonseeked: nullonseeking: nullonselect: nullonstalled: nullonsubmit: nullonsuspend: nullontimeupdate: nullontoggle: nullonvolumechange: nullonwaiting: nullonwheel: nullonauxclick: nullongotpointercapture: nullonlostpointercapture: nullonpointerdown: nullonpointermove: nullonpointerup: nullonpointercancel: nullonpointerover: nullonpointerout: nullonpointerenter: nullonpointerleave: nullonselectstart: nullonselectionchange: nullonanimationend: nullonanimationiteration: nullonanimationstart: nullontransitionend: nulldataset: DOMStringMap {}nonce: ""autofocus: falsetabIndex: 0enterKeyHint: ""onpointerrawupdate: nullnamespaceURI: "http://www.w3.org/1999/xhtml"prefix: nulllocalName: "a"tagName: "A"id: ""className: ""classList: DOMTokenList [value: ""]slot: ""attributes: NamedNodeMap {0: href, href: href, length: 1}shadowRoot: nullpart: DOMTokenList [value: ""]assignedSlot: nullinnerHTML: "qiita"outerHTML: "<a href="https://qiita.com/">qiita</a>"scrollTop: 0scrollLeft: 0scrollWidth: 0scrollHeight: 0clientTop: 0clientLeft: 0clientWidth: 0clientHeight: 0attributeStyleMap: StylePropertyMap {size: 0}onbeforecopy: nullonbeforecut: nullonbeforepaste: nullonsearch: nullelementTiming: ""previousElementSibling: nullnextElementSibling: brchildren: HTMLCollection []firstElementChild: nulllastElementChild: nullchildElementCount: 0onfullscreenchange: nullonfullscreenerror: nullonwebkitfullscreenchange: nullonwebkitfullscreenerror: nullnodeType: 1nodeName: "A"baseURI: "http://localhost:63342/memo/temp.html?_ijt=hbsc1amc7s3585dcv59sjlp0cf"isConnected: trueownerDocument: documentparentNode: div#listparentElement: div#listchildNodes: NodeList [text]firstChild: textlastChild: textpreviousSibling: nullnextSibling: brnodeValue: nulltextContent: "qiita"__proto__: HTMLAnchorElement1: br__proto__: HTMLCollection
ここでは、textを要素ノードlinkに追加した上で、この要素ノードlink、brをlist配下に追加しています。
appendChildメソッドは、insertBeforeメソッドに置き換えることもできます。
list.appendChild(link);
list.insertBefore(link, null);
insertBeforeメソッドは第1引数で指定したノードを、第2引数で指定した子ノードの直前に挿入します。
3. 属性ノードを追加する
属性ノードの設定は、属性と同名のプロパティを設定するだけです。
// <a>要素のhref属性を設定
link.href = url.value;
また、createAttributeメソッドで属性ノードを生成することもできます。
let href = document.createAttribute('href'); href.value = url.value; anchor.setAttributeNode(href);
- 属性ノードの値を設定するには、valueプロパティを使用
- 属性ノードを要素ノードに関連づけるには、appendChild, insertBeforeではなく、setAttributeNodeメソッドを使用
このやり方の方が冗長ですが、属性名を文字列で指定できるので、スクリプトから動的に属性名を変更できるメリットがあります。
既存ノードを置換、削除
先ほどの例に、
- すでにリンクがあった場合はリンクを変更
- リンクを削除できる
といった処理を追加します。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>heading.html</title> </head> <body> <form> <div> <label for="name">サイト名:</label><br /> <input id="name" name="name" type="text" size="30" /> </div> <div> <label for="url">URL:</label><br /> <input id="url" name="url" type="text" size="50" /> </div> <div> <input id="btn" name="name" type="button" value="追加" /> <input id="del" type="button" value="削除" disabled/> </div> </form> <div id="list"></div> </body> <html>
document.addEventListener('DOMContentLoaded', () => { const name = document.getElementById('name'); const url = document.getElementById('url'); const del = document.getElementById('del'); let list = document.getElementById('list'); let btn = document.getElementById('btn'); // 追加ボタンがクリックされた時の処理 btn.addEventListener('click', () => { let link = document.createElement('a'); link.href = url.value; const text = document.createTextNode(name.value); link.appendChild(text); // <div>要素配下に<a>要素が存在するか(リンクが追加されているか) if (list.getElementsByTagName('a').length > 0) { // 存在している場合はリンクを変更する list.replaceChild(link, list.lastChild); btn.value = '変更'; } else { // 存在していない場合でサイト名が入力されているかどうか if (link.text) { // 新しいリンクを追加し、削除ボタン有効、追加ボタンを変更ボタンに list.appendChild(link); del.disabled = false; btn.value = '変更'; } } }, false); // 削除ボタンがクリックされた時の処理 del.addEventListener('click', () => { // <div id="list">配下の子要素を削除し、削除ボタンを有効、変更ボタンを追加ボタンに list.removeChild(list.lastChild); del.disabled = true; btn.value = '追加'; }, false) }, false);
1. ノードを置換する
子ノードを置き換えるには、replaceChildメソッドを使用します。
list.replaceChild(link, list.lastChild);
第1引数に置き換えたいノードを、第2引数に置き換え対象のノードを指定します。
置き換え対象のノードは現在のノードに対する子ノードでならなければいけません。
2. ノードを削除する
子ノードを削除するには、removeChildメソッドを使用します。
list.removeChild(list.lastChild);
引数には、削除したい対象のノードを指定します。
こちらも同様に、対象のノードは現在のノードに対する子ノードでならなければいけません。
【js】DOM イベント操作
属性値やテキストを取得、設定する方法にについてまとめます。
コードを使用したページ
特定の属性を取得、設定
多くの属性は要素ノードの同名のプロパティとしてアクセスできます。
// 設定前 document.querySelectorAll('.tr-Item_title')[3]; // <a class=<200b>"tr-Item_title" href=<200b>"/<200b>kaya517/<200b>items/<200b>31657ec26356efaa7688"><200b>[GitHub] 東京都公式 新型コロナウイルス対策サイトがプルリク募集してる[COVID-19]<200b></a><200b> // aタグのhref属性の値を設定 document.querySelectorAll('.tr-Item_title')[3].href = 'https://b.hatena.ne.jp/'; //"https://b.hatena.ne.jp/" // 設定後 document.querySelectorAll('.tr-Item_title')[3]; //<a class=<200b>"tr-Item_title" href=<200b>"https:<200b>/<200b>/<200b>b.hatena.ne.jp/<200b>"><200b>[GitHub] 東京都公式 新型コロナウイルス対策サイトがプルリク募集してる[COVID-19]<200b></a><200b>
例えばこのようにするとqiitaの記事なのにはてぶのトップページに飛ぶようになります。
しかし、classはclassNameでないとアクセスできません。
属性とプロパティとの名前の違いを意識したくないならば、getAttribute, setAttributeメソッドを使用しましょう。
// 設定前 document.getElementById('globalHeader').className; // "st-Header" // クラス名を設定する document.getElementById('globalHeader').className = 'hogehoge'; // "hogehoge" // 設定後 document.getElementById('globalHeader').className; "hogehoge" // getAttribute, setAttributeメソッドの場合 // 設定前 document.getElementById('globalHeader').getAttribute('class'); // "hogehoge" // クラス名を設定する document.getElementById('globalHeader').setAttribute('class', 'fugafuga'); // undefined // 設定後 document.getElementById('globalHeader').getAttribute('class'); // "fugafuga"
不特定の属性を取得
特定の要素ノードに属する全ての属性を取得したい場合には、attributesプロパティを使用します。
// 全ての属性を取得 let attrs = document.getElementsByClassName('st-Header_searchInput')[0].attributes; // forEachを使うために配列に変換 attrs = Array.from(attrs); // (6) [class, placeholder, type, autocomplete, name, required] 全ての属性を表示 attrs.forEach((attr) => console.log(`${attr.name}: ${attr.value}`)); // class: st-Header_searchInput // placeholder: キーワードを入力 // type: search // autocomplete: off // name: q // required:
属性を追加、削除するには
// 新規にtitle属性を追加 // 全ての属性を取得 let attrs = document.getElementsByClassName('st-Header_searchInput')[0].attributes; attrs // NamedNodeMap {0: class, 1: placeholder, 2: type, 3: autocomplete, 4: name, 5: required, class: class, placeholder: placeholder, type: type, autocomplete: autocomplete, name: name, …} let title = document.createAttribute('title'); title.value = 'タイトル'; title // title=<200b>"タイトル" // 属性を追加 attrs.setNamedItem(title); attrs // NamedNodeMap {0: class, 1: placeholder, 2: type, 3: autocomplete, 4: name, 5: required, 6: title, class: class, placeholder: placeholder, type: type, autocomplete: autocomplete, name: name, …} // 既存のname属性を削除 // 属性を削除 attrs.removeNamedItem('name'); attrs // NamedNodeMap {0: class, 1: placeholder, 2: type, 3: autocomplete, 4: required, 5: title, class: class, placeholder: placeholder, type: type, autocomplete: autocomplete, required: required, …}
テキストを取得、設定
要素は以下のテキストを取得、設定するにはinnerHTML, textContentというプロパティを使用します。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>heading.html</title> </head> <body> <div id="text"> <p style="color: Red">設定なし</p> </div> <div id="html"> <p style="color: Red">設定なし</p> </div> </body> <html>
document.addEventListener('DOMContentLoaded', function() { document.getElementById('text').textContent = '<a href="https://qiita.com/">qiitaへのリンク</a>'; document.getElementById('html').innerHTML = '<a href="https://qiita.com/">qiitaへのリンク</a>'; }, false);
両者のプロパティに共通しているのは、
配下の子要素、テキストを完全に置き換えている
元々あった
要素は残っていません。
異なる点は、
与えられたテキストをHTML文字列として認識するかどうかです。
textContentプロパティでは、プレーンテキストとして埋め込まれるので、タグ文字列がそのまま表示されています。
innerHTMLプロパティでは、HTMLテキストを埋め込むのでリンクが有効になっています。
一般的に、HTML文字列を埋め込むのでなければ、textContentプロパティを優先して使用したほうが、高速でかつ、セキュリティー上の問題も発生しにくいです。