Boostrap系
ヘルプテキスト
テキストフィールドなどのinput要素などに注意書きとして利用できる。
Bootstrap 3 ではヘルプ表示に
.help-block
を使用していましたが、Bootstrap 4 からは.form-text
を使用することになりました。
<label for="passwd8">Password</label>
<input type="password" id="passwd8" class="form-control" aria-describedby="help8">
<span id="help8" class="form-text text-muted">
パスワードは8文字以上で入力してください。
</span>
丸いボタンを作る
クラスにrounded-pill
をつける。
btn btn-outline-secondary btn-sm rounded-pill
ナビゲーションで使うドロップボックス
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
JavaScript系
クリックされた要素を取得
jqueryの場合は$(this)
とすれば良いだけ、簡単。
$('button').on('click', function () {
console.log($(this));
});
checkboxのクリック時にチェックをつけて、他の処理も実行する
jqueryでやる場合はclick
ではなくて、change
を使わないと上手くできない。
またcheckedを要素に追加したい時はattr()
ではなくchecked()
を使う方が良き。(らしい。trueはHTMLではなく、JavaScriptだから)
$(function () {
$('.rating_star_js').on('change', function (e) {
$(this).prop('checked',true);
//他の処理
});
});
リアルタイムでテキストエリア内の文字数を表示
[html]
<p class="total_character_number">総文字数:<span id="total_character_number_js">{{$episode->character_number}}</span></p>
[JavaScript]
$("#episode_textarea_js").on("keyup", function () {
character_number = $(this).val().length;
$('#total_character_number_js').text(character_number);
});
data属性でキャメルケースを使うと小文字に変換される。
<input type="test" data-userId="{{$user->id}}" id="test">
例えば上記のようなのがあったとすると、
var test = document.querySelector("#test");
console.log(test.dataset.userid);
上記のようにuserId
ではなくuserid
になる。
特定の文字を差し引いた文字数を表示
htmlは上と同じ。
$("#episode_textarea_js").on("keyup", function () {
text = $(this).val();
character_number = text.length;
//正規表現。今回は「《・|・|・》」の4つの記号を指定。
ex = /《|\||||》/g;
ex_number = 0;
if (text.match(ex)) {
ex_number = text.match(ex).length;
}
total_character_number = character_number - ex_number;
$('#total_character_number_js').text(total_character_number);
});
リロード時にダイアログを表示する(変更内容が保存されない可能性があります。)
下記を表示したいviewファイルに個別で入力しておけばOK。
<script>
window.addEventListener('beforeunload', (event) => {
event.preventDefault();
event.returnValue = '';
});
</script>
laravelでファイルを分けてJavaScriptを使う時
window.変数名
とかにして、グローバルにする。
[html]
<button onclick="test();" class="btn btn-primary">テスト</button>
[js]
window.test = function kanaReading() {
~省略~
}
プレビュー機能で改行を表示する
preview_text = $('#episode_textarea_js').val();
preview_text = preview_text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/`/g, '`').replace(/\r?\n/g,'<br />');
$("#content_preview_article_js").html(preview_text);
下記だとhtmlエスケープされないので、注意。
preview_text = preview_text.replace(/r?n/g, '<br/>');
参考:改行コードをbrタグにして反映したいがhtmlエスケープもしたい時
選択範囲を指定した文字で囲む
今回の場合はルビ(読み仮名)機能などが必要だったので、その時に使う機能として必要でした。
[html]
//下記のボタンが重要
<button onclick="addText4();" class="btn btn-primary">ルビ(読み仮名)</button>
{{ Form::model($episode) }}
{{ Form::textarea('content', null, ['id'=>'textarea4','class'=>'novel_content_textarea mt-3','row'=>'20','placeholder'=>'エピソードの本文を入力してください。']) }}
{{ Form::close() }}
[js]
window.addText4 = function addText4() {
//テキストエリアと挿入する文字列を取得
var area = document.getElementById('textarea4');
var text1 = '('
var text2 = ')《ここにルビ》'
//カーソルの開始位置と終了位置を基準に分割
area.value = area.value.substr(0, area.selectionStart) +
text1 +
area.value.substr(area.selectionStart, area.selectionEnd - area.selectionStart) +
text2 +
area.value.substr(area.selectionEnd);
}
アニメーション
スイッチボタン
[html]
<div id="switchArea">
<input type="checkbox" id="switch1">
<label for="switch1" id="label_switch"><span></span></label>
<label for="switch1" id="swImg"></label>
</div>
[css]
/* === ボタンを表示するエリア ============================== */
# switchArea {
position: relative;
margin: 0;
width: 102px;
background: $back_blue;
border-radius: 17px;
/* === チェックボックス ==================================== */
input[type="checkbox"] {
display: none;
/* === チェックボックスのラベル(ONのとき) ================ */
&:checked+#label_switch {
border-color: #78bd78;
/* 選択タブの枠線 */
}
/* === 丸部分のSTYLE(ONのとき) =========================== */
&:checked~#swImg {
transform: translateX(68px);
background: #78bd78;
border-color: #78bd78;
;
}
}
/* === チェックボックスのラベル(標準) ==================== */
label {
display: block;
box-sizing: border-box;
height: 34px;
border: 2px solid #999999;
border-radius: 17px;
}
/* === 丸部分のSTYLE(標準) =============================== */
#swImg {
position: absolute;
width: 26px;
height: 26px;
background: #999999;
top: 4px;
left: 4px;
border-radius: 13px;
transition: .2s;
}
}
radioボタンで使ったアニメーション
[html]
<div class="genre-radio form-group">
<div class="box">
<input type="radio" id="genre1" name="genre">
<label for="genre1">異世界ファンタジー</label>
<input type="radio" id="genre2" name="genre">
<label for="genre2">現代ファンタジー</label>
<input type="radio" id="genre3" name="genre">
<label for="genre3">ウサギ</label>
<input type="radio" id="genre4" name="genre">
<label for="genre4">トリ</label>
</div>
</div>
[sass]
.genre-radio {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.genre-radio:before,
.genre-radio:after {
-webkit-box-sizing: inherit;
box-sizing: inherit;
}
.genre-radio .box {
width: 100%;
margin: 2em auto;
text-align: left;
border: 1px solid white;
border-radius: 3px;
background: #ffffff;
}
.genre-radio input[type=radio] {
display: none;
}
.genre-radio label:focus,
.genre-radio label:hover,
.genre-radio label:active,
.genre-radio input:checked+label {
color: #da3c41;
}
.genre-radio label:focus:before,
.genre-radio label:hover:before,
.genre-radio label:active:before,
.genre-radio input:checked+label:before {
border-color: #da3c41;
background: #ffffff;
}
.genre-radio label {
font-size: 1em;
font-weight: bold;
line-height: 1;
position: relative;
display: block;
overflow: hidden;
padding: 1em 1em 1em 3em;
cursor: pointer;
-webkit-transition: all 0.15s ease;
transition: all 0.15s ease;
white-space: nowrap;
text-overflow: ellipsis;
background: #ffffff;
}
.genre-radio label:before {
position: absolute;
top: 1em;
left: 1em;
width: 10px;
height: 10px;
content: '';
border: 0.2em solid #cccccc;
border-radius: 50%;
}
.genre-radio input:checked+label:before {
border-color: #da3c41;
background: #da3c41;
}
.genre-radio input:disabled+label {
cursor: not-allowed;
color: rgba(0, 0, 0, 0.5);
background: #efefef;
}
.genre-radio input:disabled+label:hover {
border-color: rgba(0, 0, 0, 0.1);
}
.genre-radio input:disabled+label:before {
border-color: #ffffff;
background: #ffffff;
}
アコーディオン
[html]
<div class="section s_01">
<div class="accordion_one">
<div class="accordion_header">オプション設定(任意)<div class="i_box"><i class="one_i"></i></div>
</div>
<div class="accordion_inner">
<div class="box_one">
<div class="txt_a_ac">
<div class="genre-radio-wrapper box-border">
//ここに開いた時に表示したい内容
</div>
</div>
</div>
</div>
</div>
</div>
[sass]
// アコーディオン
.s_01 .accordion_one {
max-width: 1200px;
margin: 0 auto;
}
.s_01 .accordion_one .accordion_header {
background-color: $border_color;
color: #fff;
font-size: 26px;
font-weight: bold;
padding: 20px 11%;
text-align: center;
position: relative;
z-index: +1;
cursor: pointer;
transition-duration: 0.2s;
}
.s_01 .accordion_one .accordion_header:hover {
opacity: .8;
}
.s_01 .accordion_one .accordion_header .i_box {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
right: 5%;
width: 40px;
height: 40px;
border: 1px solid #fff;
margin-top: -20px;
box-sizing: border-box;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
transform-origin: center center;
transition-duration: 0.2s;
}
.s_01 .accordion_one .accordion_header .i_box .one_i {
display: block;
width: 18px;
height: 18px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
transform-origin: center center;
transition-duration: 0.2s;
position: relative;
}
.s_01 .accordion_one .accordion_header.open .i_box {
-webkit-transform: rotate(-360deg);
transform: rotate(-360deg);
}
.s_01 .accordion_one .accordion_header .i_box .one_i:before,
.s_01 .accordion_one .accordion_header .i_box .one_i:after {
display: flex;
content: '';
background-color: #fff;
border-radius: 10px;
width: 18px;
height: 4px;
position: absolute;
top: 7px;
left: 0;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
transform-origin: center center;
}
.s_01 .accordion_one .accordion_header .i_box .one_i:before {
width: 4px;
height: 18px;
top: 0;
left: 7px;
}
.s_01 .accordion_one .accordion_header.open .i_box .one_i:before {
content: none;
}
.s_01 .accordion_one .accordion_header.open .i_box .one_i:after {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.s_01 .accordion_one .accordion_inner {
display: none;
padding: 30px 30px;
border-left: 1px solid $border_color;
border-right: 1px solid $border_color;
border-bottom: 1px solid $border_color;
box-sizing: border-box;
}
@media screen and (max-width: 1024px) {
.s_01 .accordion_one .accordion_header {
font-size: 18px;
}
.s_01 .accordion_one .accordion_header .i_box {
width: 30px;
height: 30px;
margin-top: -15px;
}
}
@media screen and (max-width: 767px) {
.s_01 .accordion_one .accordion_header {
font-size: 16px;
text-align: left;
padding: 15px 60px 15px 15px;
}
}
[js]
// .s_01 .accordion_one
$(function () {
//.accordion_oneの中の.accordion_headerがクリックされたら
$('.s_01 .accordion_one .accordion_header').click(function () {
//クリックされた.accordion_oneの中の.accordion_headerに隣接する.accordion_innerが開いたり閉じたりする。
$(this).next('.accordion_inner').slideToggle();
$(this).toggleClass("open");
});
});
小ネタ・その他
widthの幅を正確に合わせてもうまく収まらない現象の解決方法
例えば、width100%になるように、4つの要素に25%としてもはみ出てしまう現象がある。
これはHTMLの仕様でちょっと差が出たりするかららしい。
そのため、下記のようにすればうまくいく。
//親要素
letter-spacing: -1em; // 文字間を詰める
//子要素
letter-spacing: normal; //文字間を元に戻す
Choromeのオートコンプリート(入力履歴が表示されるやつ)を止める。or背景色を帰る。
autocomplete属性
をオフにする。
またはCSSで背景色を変更したりもできる。
<form autocomplete="off">
</form>
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0px 1000px [背景にしたい色] inset;
}
参考:【Chrome】オートコンプリートのとき、背景を黄色にしない
星の評価機能を追加する
星をクリックしたら評価を送信する機能を実装したいことが多くのwebアプリではあるはず。
そこで今回はいい感じのを実装してみました。
とりあえず、下記をまるっとコピペすればいい感じの星評価が実装できます。
<p class="col-10 mx-auto hochi_title">スターで評価</p>
{{ Form::open(['route'=>['comment.store','episode'=>$episode->id],'id'=>'star_rating_form_js']) }}
<div class="rating">
@for ($i = 5; $i >= 1 ; $i--)
<input type="radio" name="rating" value="{{$i}}" id="r{{$i}}" class="rating_star_js" data-postid={{$episode->id}}
{{$existRating && $existRating->rating == $i ? 'checked' : ''}}>
<label for="r{{$i}}"></label>
@endfor
</div>
{{ Form::close() }}
<div class="col-md-8 mx-auto alert alert-warning display_none_js">
<p id="rating_announce_js" class="rating_p">評価を送信しました。</p>
</div>
public function ajasPostStarRating(Request $request)
{
$request->validate([
'rating'=>'required|integer'
]);
$auth = Auth::user();
$novel_id = Episode::find($request->episode_id)->novel->id;
$existRating = Rating::where('user_id', $auth->id)->where('novel_id', $novel_id)->first();
if ($existRating) {
$existRating->update([
'rating'=>$request->rating,
]);
} else {
$existRating = Rating::create([
'rating'=>$request->rating,
'user_id'=>$auth->id,
'novel_id'=>$novel_id
]);
}
$json = [
'existRating' => $existRating
];
return response()->json($json);
}
@mixin star-rating($filled-color: #F9BF3B, $empty-color: #444, $size: 40px, $width: 300px) {
$star--filled: ★;
$star--empty: ☆;
width: $width;
>* {
float: right;
}
// optional initial pulse of stars
@at-root {
@keyframes pulse {
50% {
color: lighten($empty-color, 10%);
text-shadow: 0 0 15px lighten($empty-color, 20%);
}
}
}
label {
height: 40px;
width: 20%;
display: block;
position: relative;
cursor: pointer;
@for $i from 5 through 1 {
&:nth-of-type(#{$i}):after {
$animation-delay: $i * .05s;
animation-delay: $animation-delay;
}
}
&:after {
transition: all 0.4s ease-out;
-webkit-font-smoothing: antialiased;
position: absolute;
content: '#{$star--empty}';
color: $empty-color;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
font-size: $size;
animation: 1s pulse ease;
}
&:hover:after {
color: lighten($empty-color, 10%);
text-shadow: 0 0 15px lighten($empty-color, 10%);
}
}
input {
display: none;
&:checked {
+label:after,
~label:after {
content: '#{$star--filled}';
color: $filled-color;
text-shadow: 0 0 20px $filled-color;
}
}
}
}
.rating {
width: 400px;
height: 70px;
margin: 0 auto;
@include star-rating();
}
.rating_p {
margin-bottom: 0;
padding: 0;
text-align: center;
}
$(function () {
$('.rating_star_js').on('change', function (e) {
episodeId = $(this).data('postid');
rating = $(this).attr("value");
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url: '/ajasPostStarRating', //routeの記述
type: 'POST', //受け取り方法の記述(GETもある)
data: {
//コントローラーに渡すパラメーター
'episode_id': episodeId,
'rating':rating,
},
})
// Ajaxリクエストが成功した場合
.done(function (data) {
$(this).prop('checked', true);
$('.display_none_js').fadeIn();
$('#rating_announce_js').text(data.existRating.rating + 'つ星の評価を送信しました。');
})
// Ajaxリクエストが失敗した場合
.fail(function (data, xhr, err) {
//ここの処理はエラーが出た時にエラー内容をわかるようにしておく。
//とりあえず下記のように記述しておけばエラー内容が詳しくわかります。笑
console.log('エラー');
console.log(err);
console.log(xhr);
});
return false;
});
});
pタグやaタグで文字の高さがおかしい
line-height
で調整すればOK。
Laravelがなんか重たいなぁと思った時に確認すること
・bladeの継承を二重にしていないか。
・N+1問題が発生していないか。
たぶん、初心者の場合はこの二つが原因の可能性大。
ベンダーフレフィツクスについて【自動的に付ける方法も】
主なベンダープレフィックスは下記の通り。
-moz- …… Firefox
-webkit- …… Google Chrome、Safari
-o- …… Opera
-ms- …… Internet Explorer
自動的にベンダープレフィックスを付けてくれるサービス。
Autoprefixer CSS online
こちらを利用すれば、いい感じにしてくれる。便利。
各ブラウザの対応状況を調べたい場合は「Can I use」というサービスを利用すれば調べられる。
aタグで遷移させない方法
下記でOK。すごく簡単。
<a href="javascript:void(0)">遷移しないよ</a>
でもこの方法はあまりよろしく無いらしい。
なので、下記のようbuttonタグで書き換えるのがオススメ。
<button type="button">遷移しないよ</button>