20200305のHTMLに関する記事は6件です。

Javascriptでいろいろな正星型図形(n芒星)を生成する

仕組み

図形生成の仕組みは、こちらのページの技術を応用したもの + 高校で習った三角関数。
そもそも六芒星をはじめとする星型図形にはどれだけ複雑なものであっても、線の本数と傾きは正多角形の時と変わらないという法則がある。つまり、線の傾きと位置さえ調整してやれば、煩雑な多角形であっても生成可能なのである。
で、その線の位置と長さを求めるときに使うのが、いわゆるsin、cos、tanなのだ。

図にするとこんな感じ。
1583417842156.jpg

赤線の部分がいわゆる「斜辺」と呼ばれるもの。斜辺の長さは50%=0.5で、赤線と赤線の間の角(中心角)は今回の場合5等分(赤い点=円の中心から接線に向けて伸びる直線、つまり半径なのだ)。これにsinやcosをかけてやれば値がwidthとheightの長さが求まるという寸法だ。

高校を卒業したての人に向けて言えば、「width=弦の長さ」。と言えばイメージしやすいのではないだろうか。
ちなみに、sin、cos、tanは角度をもとに線の長さを求める式である。

作例例

1583418314284.jpg
オーソドックスな六芒星。

1583416971266.jpg
第三引数(intersection)を0にすれば通常の正多角形も作図できる。

1583418677837.jpg
同じ7角形だが、第三引数を2にしてある。

1583417002963.jpg
こんな複雑なのも可能。ちなみに、400角形で頂点を188個飛ばしにつないでいる。

1583418439057.jpg
1024角形。邪神アバター呼ばわりされた。

ちなみに、星型多角形は頂点の数が増えてゆくごとに、作れる"星"の種類も増えてゆく。
最大数は(頂点数÷2)-1 以下の数。たとえば、7角形なら、7÷2=3.5、3.5-1=2.5なので、2つ作れることになる。

コード本体

html
<style>
/* position:relative、position:absolute、position:fixedのいずれか+タテヨコの高さが決まっていないと崩れるので注意。 */
#torirobyte{
    position: relative;
    width: 600px;
    height: 600px;
}   
/* border-bottomで線を表現しているので、線の太さや色はここをいじれば制御できる。 */
#torirobyte div{
    border-bottom: 1px solid black;
}
</style>


<div id="torirobyte">
</div>

<script type="text/javascript">
(function(){

var target = document.getElementById('torirobyte');
CreateGrams(target, 12, 3);

function CreateGrams(parantelm, vertex, intersection){
    // vertexは頂点数。
    // intersectionは点と点をつなぐ直線が何個飛ばしに線を作るか。0の場合、普通の正多角形ができあがります。
    // elementの中に整形されます。

    // 図形のパラメーターの設定
    var radius = 50; // 半径(widthとheightはパーセント指定するので一律50)
    var slope_per_one = 360 / vertex; // 線の傾き
    var center_angle = slope_per_one * (intersection + 1); // 中心角。この値によって、完成する星型図形が変化します。
    var radian_slope = ((180 - center_angle) / 2) * (Math.PI / 180); // sin、cosに使う角(ラジアン単位)
    var width_rate = Math.cos(radian_slope) * radius * 2; // 線に設定するwidth(単位は%)
    var height_rate = Math.sin(radian_slope) * radius * 2; // 線に設定するheight(単位は%)
    var leftposision = (100 - width_rate) / 2; // positionのleftに付ける値。これで横位置を要素内におさめる

    // 図形の生成
    parantelm.innerHTML = '';
    for(var int = 0; int < vertex; int++){
        var childs = document.createElement('div');

        // 図形逆向き化防止
        if(intersection % 2 == 0){
            var slope_current = (slope_per_one * int);
        }else{
            var slope_current = (180 + (slope_per_one * int));
        }

        childs.style.margin = 'auto';
        childs.style.position = 'absolute';
        childs.style.top = 0;
        childs.style.bottom = 0;
        childs.style.left = leftposision + '%';
        childs.style.transform = 'rotate(' + slope_current + 'deg)';
        childs.style.width = width_rate + '%';
        childs.style.height = height_rate + '%';
        parantelm.appendChild(childs);
    }
    return parantelm;
}

})();
</script>

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

【Bootstrap】アコーディオンが1つの時にレイアウトが崩れた話

概要

DjangoでWebアプリケーションを作成中、フロントのCSSはBootstrapを使ってデザインしていた。

viewから渡されたリストをループで処理しながらアコーディオンを作成しようとしたら、
リストが1件の時の表示がおかしくなってしまった。

ループが2件以上の時(正常):
スクリーンショット 2020-03-05 22.31.03.png

1件の時(下線切れてるし角が丸くない):
スクリーンショット 2020-03-05 22.32.04.png

ソース:

test.html
<div class="accordion" id="accordion" role="tablist" aria-multiselectable="true">
    {% for l in list %}
    <div class="card">
        <div class="card-header" role="tab" id="heading{{ forloop.counter }}">
            <h5 class="mb-0">
                <a class="text-body d-block p-3 m-n3" data-toggle="collapse" href="#collapse{{ forloop.counter }}" role="button" aria-expanded="true" aria-controls="collapse{{ forloop.counter }}">
                    {{ forloop.counter }}
                </a>
            </h5>
        </div>
        <div id="collapse{{ forloop.counter }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ forloop.counter }}" data-parent="#accordion">
            <div class="card-body">
                {{ l }}
            </div>
        </div>
    </div>
    {% endfor %}
</div>

今のところの対処法

リストが1件しかない場合の分岐処理を追加し、CSSのクラスを追加してあげる。

card
<div class="card">

↑に

card-border-rounded
<div class="card {% if list|length == 1 %}border rounded{% endif %}">

↑という分岐を追加してあげると1件でも綺麗に表示してくれる。

スクリーンショット 2020-03-05 22.39.47.png

おわりに

これ解決するのに1〜2時間かけた気がする...

Djangoじゃなくてもリストのサイズがわかれば他の言語/フレームワークでもできると思うし、なんならもっといい方法がある気がする。

誰かBootstrap/CSSに詳しい人いたらもっとスマートな方法教えてください...(涙目)

ちなみにBootstrap公式ドキュメントにあるソースのアイテム2と3を全部消してアイテム1だけ残しても同じ現象が起きるから、アコーディオンが1個だけなんて使い方は想定してないんじゃないかなぁ...と勝手に思ったり。

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

formにsubmit先が異なる複数のボタンを設置する

1つのformに送信先が異なる複数のボタンを設置する必要があったのですが、盛大にハマってしまったので備忘録です。

方法

index.html
<form method="POST" action="?">
  <input type="checkbox" name="tests[]" value="test1" />test1
  <input type="checkbox" name="tests[]" value="test2" />test2
  <input type="checkbox" name="tests[]" value="test3" />test3
  <button type="submit" name="_method" value="post" formaction="送信先のアドレス"></button>
  <button type="submit" name="_method" value="delete" formaction="送信先のアドレス"></button>
</form>

解説

ポイントはformタグのaction属性値を「?」にすることです。

formタグ内にsubmitボタンが1つの場合は、「送信先のアドレス」を設定しますが、
今回のような複数のsubmitボタンを用意する場合は、「送信先のアドレス」をbuttonタグのformaction属性に設定します。

ちなみにこのformaction属性はHTML5から使えるようになりました。

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

HTMLの基本

head要素

ウェブサイトの情報や、参照するCSSファイルを記載する部分です。ここに記述した情報は画面上には表示されない。

body要素

画面上に表示したい情報を記載するところ。
要素はタグというものに囲まれて表現される。

タグ

HTMLにおける要素のかたまりを示す記述です。
開始タグと終了タグで囲むことによって、コード全体のうち、どこからどこまでがhead要素で、どこからどこまでがbody要素か判断することができる。また、終了タグには/がある点に注意する。

<!DOCTYPE HTML>

この文章がHTML文章であることを宣言する要素。閉じタグはない。
この要素を記述していないと、レイアウトが崩れたり、表示がされない場合がある。

<html>...</html>

HTML文章の始まりと終わりを示す要素。

<head>...</head>

ウェブサイトの情報を記述する部分。

...

「特定の意味が無い」要素。汎用的に様々な用途で使うことができ、レイアウトを作成する際に重宝される。
classセレクタを付与して使用。

...

フッターを示す専用の要素。
コメント数やコピーライトをfooter要素で囲うことによって、下部にあることがHTMLだけでわかるようになる。
footer要素は、ブロックレベル要素。

<meta>

文章に関する情報を指定する時に使用する。閉じタグが無い要素なので、注意する。
後に続く、charset="UTF-8"は文字コードを指定するもので、この記述が無いと文字化けしてしまう。

<title>...</title>

ウェブサイトのタイトルを記述する場所。ここにタイトルを記述してもウェブサイトの画面上には表示されない。
Googleでの検索結果や、ブラウザのタブに表示するタイトルとして使用される。

<link rel="stylesheet">

この記述によって、対応するCSSファイルを指定することができる。hrefには実際のファイル名を記載する。
<link rel="stylesheet" "href="参照すべきCSSのファイル名を指定">

<h1>...</h1>

文章の見出しを指定するもの。
<h1>.../<h1>で囲むと見出しと判断されて文字が太く大きく表示される。

<p>...</p>

文章の段落を表すもの。
<p>...</p>で囲むとひとかたまりの文章として認識される。

<b>...</b>

文字を太くする要素。<b>...</b>で囲まれている部分だけ太字になります。
上記のような要素はたくさんのものが存在する。

ブロックレベル要素

ウェブデザインにおける箱となる要素の事。
<h1>...</h1>や<p>...</p>はブロックレベル要素。

インライン要素

ウェブデザインにおける箱にはならず、主には文字の修飾などに使われるもの。
<b>...</b>はインライン要素です。

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

Javascriptで全部のDOMにアクセスしたい時

しょっちゅう書いてるのに忘れるからメモ
再帰的に処理するだけ

const getRecursionNodes = node => {
  node = node.firstChild;
  while (node) {
    getNodes(node);
    node = node.nextSibling;
  }
};

フレームワーク内で書いたら負け。
node.jsでスクレイピングしたい時とかにね

例えばこんなDOMっぽい文字列一部だけでもいいし全部でもいいし
axiosとかで普通に取得する。

const tagText = `
<div>
  <div>
    タイトル
    <span>サブタイトル</span>
  </div>
</div>
`;

で文字列をパースする

  const parser = new DOMParser();
  const doc = parser.parseFromString(tagText, 'text/html');
  getRecursionNodes(doc.getElementsByTagName('body')[0]);

こんな風に書くとgetRecursionNodesのwhile内で
body内の全要素をぶん回して処理できる。

parseFromStringはDOM Tree一部だけのつもりでも
htmlとbodyタグを勝手に付与しやがるので重複に注意。

空DOMをすっ飛ばしたい時はnextElementSiblingを使うと少し軽くなるかも。
この書き方だと文字列だけのnodeとかを処理したい時は
node.nodeTypeで判定してあげる必要がある。
https://developer.mozilla.org/ja/docs/Web/API/Node/nodeType

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

【Python】ふと、お問い合わせフォームを作りたくなったら

はじめまして、普段はWebサービスの開発をしてます。

はじめに

先日、ふと、お問合わせフォームを用意したくなり、
さらに、その内容を自身のメールアドレス宛に送信したくなったので、
Pythonで実装しました。

全体像

本記事では、Djangoを使用します。
Djangoとても便利です。

本文を追加.png

フォーム定義

フォーム定義はforms.pyで行います。

基本的には、Django標準機能をインポートして作ります。

加えて、フォームの項目を定義します。
「名前」、「連絡先」、「お問い合わせ内容」みたいなやつですね。
各項目の必須設定や文字制限も定義できます。

サンプルコード
# Django標準機能を import
from django import forms
from django.core.mail import BadHeaderError, send_mail
from django.http import HttpResponse
# Django設定をimport
from django.conf import settings


class ContactForm(forms.Form):
    # フォーム項目として"お名前"を定義
    name = forms.CharField(
        label='',
        max_length=100,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': "お名前",
        }),
    )
    # フォーム項目として"メールアドレス"を定義
    email = forms.EmailField(
        label='',
        widget=forms.EmailInput(attrs={
            'class': 'form-control',
            'placeholder': "メールアドレス",
        }),
    )
    # フォーム項目として"お問い合わせ内容"を定義
    message = forms.CharField(
        label='',
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'placeholder': "お問い合わせ内容",
        }),
    )

    # メールを送信する
    def send_email(self):
        subject = "お問い合わせ"
        message = self.cleaned_data['message']
        name = self.cleaned_data['name']
        email = self.cleaned_data['email']
        from_email = '{name} <{email}>'.format(name=name, email=email)
        # 受信者リストを指定
        recipient_list = [settings.EMAIL_HOST_USER]
        try:
            send_mail(subject, message, from_email, recipient_list)
        except BadHeaderError:
            return HttpResponse("無効なヘッダが検出されました。")

処理定義

処理の定義はviews.pyで行います。

こちらも、Django標準機能をインポートして作ります。

加えて、上記のフォーム定義もインポートします。
また、表示させる HTML遷移する URLなどを指定します。

サンプルコード
# Django標準機能をimport
from django.urls import reverse_lazy
from django.views.generic import TemplateView
from django.views.generic.edit import FormView

# forms.py からフォーム定義をimport
from .forms import ContactForm

# submitイベントの挙動を定義
class ContactFormView(FormView):
    # 表示させる html を指定
    template_name = 'contact_form.html'
    # form.py で定義したクラス名を指定
    form_class = ContactForm
    # 遷移後のurlを引数で指定
    success_url = reverse_lazy('contact_result')

    # メールを送信
    def form_valid(self, form):
        form.send_email()
        return super().form_valid(form)

# フォーム送信後の挙動を定義
class ContactResultView(TemplateView):
    # 表示させる html を指定
    template_name = 'contact_result.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['success'] = "お問い合わせは正常に送信されました。"
        return context

画面定義

お問い合わせ画面 と お問い合わせ送信後の画面 を用意します。

お問い合わせ画面では{{ form.as_p }}を使って、
フォーム定義で定義した項目を<p>タグでレンダリングします。

お問い合わせ画面
contact_form.html
<h4>お問い合わせ</h4>
  <div>
    <p><br>必要事項を入力して下さい。</p>
    <form method="POST">{% csrf_token %}
      {{ form.as_p }}
      <button type="submit" class="btn">送信</button>
      <a href="{% url 'top' %}">トップに戻る</a>
    </form>
  </div>
</div>

お問い合わせ送信後の画面
contact_result.html
<div>
  <p>
    お問い合わせ 送信完了
  </p>
  <div>
    <p>
      お問い合わせありがとうございました。<br>
      内容を確認のうえ、回答させて頂きます。
    </p>
    <a href="{% url 'top' %}">トップに戻る</a>
  </div>
</div>

URL定義

urls.pyURL と処理の紐付けを定義します。

サンプルコード
# 処理クラスをimport
# <PROJECT_NAME>は自身のプロジェクト名
from <PROJECT_NAME>.views import ContactFormView, ContactResultView

urlpatterns = [
    # ...
    path('contact/', ContactFormView.as_view(), name='contact_form'),
    path('result/', ContactResultView.as_view(), name='contact_result'),
]

メール情報定義

最後に、settings.py でプログラムで使用するメール情報を定義します。
SMTPサーバーやポート番号、ユーザー情報を指定します。

サンプルコード
EMAIL_HOST = 'smtp.XXX.com'
EMAIL_PORT = XXX
EMAIL_HOST_USER = 'XXX@XXX.com'
EMAIL_HOST_PASSWORD = 'XXX'
EMAIL_USE_TLS = True

まとめ

Django標準機能を使用することで、簡単にお問い合わせフォームが作成できました。
ふと、お問い合わせフォームを作りたくなった時の参考になれば幸いです。

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