コールバック関数・some
map.delete(キー) array.splice(index, 個数(なしの場合はindex以降すべて)) 削除
fabric class継承
fabric (ダブルクリック グループ化)
obj.clickedはundefind → elseを通ってtrue(0.5秒後にfalseになる) → 0.5秒以内に再度クリックするとコールバック関数が実行される。
コールバックではungroupされたあとにisTextGroup()で解除された個々のオブジェクトにitextがあればtrue
→クリックしたオブジェクトがアクティブ(フォーカス当て、編集モード)になる
非アクティブになったら再度グループ化(itextにediting:exitedイベントでグループ化する処理を別で記載)。
jsonをオブジェクトで持たせてmap構造に変換する
画像アップロード
画像アップロード(ajax)
clone(true) 要素のクローンを作成し、選択状態
find(子要素) 子要素を取得
リクエスト(ajax)
当たり判定
モーダル
css
/*モーダル本体の指定 + モーダル外側の背景の指定*/
.modal-view {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
background: rgba(0, 0, 0, 50%);
padding: 40px 20px;
overflow: auto;
opacity: 0;
visibility: hidden;
box-sizing: border-box;
z-index: 999;
opacity: 0;
}
/*モーダル本体に「active」クラス付与した時のスタイル*/
.modal-view-conf.active {
visibility: visible;
opacity: 1;
transition: .3s;
}
.modal-body-wrap {
position: relative;
}
/*モーダル枠の指定*/
.modal-body {
position: absolute;
display: block;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
max-width: 900px;
width: 70%;
height: 300px;
}
/*モーダルを閉じるボタンの指定*/
.modal-close {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
top: -40px;
right: -40px;
width: 40px;
height: 40px;
font-size: 40px;
color: #fff;
cursor: pointer;
}
/*モーダル内のコンテンツの指定*/
.modal-content {
background: #fff;
text-align: left;
padding: 30px;
}
.headline_table {
border-collapse: separate;
border-top: 1px solid #DDD;
border-left: 1px solid #DDD;
}
.headline_table td {
border-right: 1px solid #DDD;
border-bottom: 1px solid #DDD;
height: 20px;
padding-top: 0px;
padding-bottom: 0px;
}
html
<div class="modal-view-conf">
<div class="modal-body">
<!-- 閉じるボタン -->
<div class="modal-close">×</div>
<!-- モーダル内のコンテンツ -->
<div class="modal-content">
<table class="headline_table" style="width:100%; margin: 1% 0; border-collapse:collapse; white-space:nowrap;">
<tr>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
</tr>
<tr>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
</tr>
<tr>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
</tr>
<tr>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
</tr>
<tr>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
</tr>
</table>
</div>
</div>
</div>
javascript
//開くボタンをクリックしたらモーダルを表示する
$('.view-conf').on('click', function(e) {
e.preventDefault();
$('.modal-view-conf').addClass('active');
return false;
});
//閉じるボタンをクリックしたらモーダルを閉じる
$('.modal-close').on('click', function() {
$('.modal-view-conf').removeClass('active');
});
$('.cancel').on('click', function() {
$('.modal-view-conf').removeClass('active');
});
//モーダルの外側をクリックしたらモーダルを閉じる
$('.modal-view-conf').on('click', function(e) {
if (!$(e.target).closest('.modal-content').length) {
$('.modal-view-conf').removeClass('active');
}
console.log('クリック', $(e.target).closest('.modal-content'));
});
$(e.target).closest(‘.modal-content’)
$(document).on('click', function(e) {
if (!$(e.target).closest('特定の要素のクラス').length) {
// フェードやスライドなどの処理方法を記述;
}
});
まずe.targetでイベントが発生した要素を取得します。
「closest」でその最も近い親要素を指定し、そこに特定の要素があるかを「length」で確認しています。ない場合はlength 0
つまり、クリックした要素の上に特定の要素(親要素)がない場合、
フェードやスライドなどの処理方法を実行という方法になります。
ajax基本
function updateContent(data, id) {
const URL = '/admin/admission/result_card/get_preview_html';
$.ajax({
url: URL,
type: 'POST',
dataType: 'json',
data: {
'report_id': id,
'csrf_token_name': $('input[name="csrf_token_name"]').val(),
}
}).done(function(result) {
if (result.error_message) {
window.alert(result.error_message);
return;
}
data.content = result.preview_html
return;
}).fail(function(jqXHR, textStatus, errorThrown) {
// console.log(jqXHR, textStatus, errorThrown);
});
}
要素を追加
function addItem(element) {
let clone = $(element).parent().find('div.tmp_select').clone();
let html = $(clone).html();
html = '<div class="add_select">' + html +'</div>'
if($(element).parent().find('div.add_select').length == 0) {
$(element).parent().find('div.tmp_select').after(html);
} else {
$(element).parent().find('div.add_select:last').after(html);
}
}
function addItem2(element) {
let clone = $(element).parent().find('div.tmp_select').clone();
let html = $(clone).html();
html = '<div class="add_select sub">' + html +'</div>'
$(element).parent().find('div.tmp_select').after(html);
}
Ajax(FORM_DATA or 通常)
function submitForm(element) {
var target = $(element).closest('form').attr('id');
var edit = $(element).closest('.edit');
var preview = $(element).closest('.edit').prev('.preview');
var params = ''
//総合的な探求の時間の内容・評価 jsonに整形
if(target == 'sogo_content_evaluate'){
var data = [];
for (var i = 0; i < 3; i++) {
var item_array = new Array();
let item_list = new Object();
var key = i + 1 ;
item_list.type = key;
$('.add_select').find('select[name="show_yoroku_sogo_manual_select'+key+'[]"]').each(function(inx,elm){
var item = $(elm).val();
item_array.push(item);
})
$('select[name="show_yoroku_sogo_manual_select'+key+'[]"]').each(function(inx,elm){
$(elm).prop('disabled',true);
})
item_list.items = item_array;
if(item_list.items.length == 0){
}else{
data.push(item_list);
}
}
var json = JSON.stringify(data)
$('input[name="show_yoroku_sogo_manual_select"]').val(json);
}
let useFormData = false;
let contentType = false;
if(target == 'tokubetu_record') {
params = formData(target, edit);
} else if(target == 'doutoku_record'){
params = formData(target);
} else if(target == 'shoken_record') {
params = formData(target, edit);
} else if(target == 'attendence_record') {
params = formData(target);
} else if(target == 'subject_bikou_record') {
params = formData(target, edit);
} else {
useFormData = true ;
contentType = 'application/x-www-form-urlencoded; charset=UTF-8';
params = $('form#'+target).serialize();
}
if(!params) {
return;
}
$.ajax({
url: '/admin/report/conf/yoroku_conf_edit',
type: 'POST',
dataType: 'json',
data: params,
contentType: contentType ,
processData: useFormData ,
}).done(function (result) {
// エラーメッセージがなければ
if ($(result.error).length == 0) {
//更新箇所とメッセージを保存
localStorage.clear();
localStorage.setItem("target",target);
localStorage.setItem("message",result.message);
//load function発火
window.location.reload();
} else {
//エラーメッセージを表示
localStorage.clear();
$(edit).find('.msg').empty();
$(preview).find('.msg').empty();
$(result.error).each(function (index, error_message) {
$(edit).find('.msg').append('<p>' + error_message + '</p>');
})
if(target == 'sogo_content_evaluate'){
for (var i = 1; i <= 3; i++) {
$('select[name="show_yoroku_sogo_manual_select'+i+'[]"]').each(function(inx,elm){
$(elm).prop('disabled',false);
})
}
}
}
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR, textStatus, errorThrown);
});
}
function validsSameValue(array) {
let set = new Set(array);
console.log(set.size, array.length);
return set.size !== array.length;
}
function formData(target, edit = null) {
const FORM_DATA_MAP = new Map();
const FORM_DATA = new FormData();
const SELECT_ITEM_MAP = new Map();
const SELECT_ARRAY = new Array();
FORM_DATA.append('use_form_data', target);
if(target == 'tokubetu_record') {
FORM_DATA_MAP.set('show_yoroku_tokubetu_manual_type', $('#' + target).find('input:radio[name="show_yoroku_tokubetu_manual_type"]:checked').val());
FORM_DATA_MAP.set('show_yoroku_tokubetu_manual_text', $('#' + target).find('textarea[name="show_yoroku_tokubetu_manual_text"]').val());
let result_array = new Array();
$('#' + target).find('.add_select select[name="show_yoroku_tokubetu_manual_select"] option:selected').each(function(index, data){
result_array.push($(data).val());
});
let isExisted = validsSameValue(result_array);
if(!isExisted) {
$('#' + target).find('.add_select select[name="show_yoroku_tokubetu_manual_select"] option:selected').each(function(index, data){
SELECT_ARRAY.push($(data).val());
});
} else {
$(edit).find('.msg p').remove();
$(edit).find('.msg').append('<p>同じ項目が複数選択されています。</p>');
return false;
}
SELECT_ITEM_MAP.set('items', SELECT_ARRAY)
FORM_DATA_MAP.set('show_yoroku_tokubetu_manual_select', JSON.stringify(Object.fromEntries(SELECT_ITEM_MAP)));
FORM_DATA_MAP.set('show_yoroku_tokubetu_manual_empty', Number($('#' + target).find('input[name="show_yoroku_tokubetu_manual_empty"]').prop('checked')));
FORM_DATA.append('yoroku_tokubetu_manual', JSON.stringify(Object.fromEntries(FORM_DATA_MAP)));
} else if(target == 'doutoku_record') {
FORM_DATA_MAP.set('show_yoroku_doutoku_manual_type', $('#' + target).find('input:radio[name="show_yoroku_doutoku_manual_type"]:checked').val());
FORM_DATA_MAP.set('show_yoroku_doutoku_manual_text', $('#' + target).find('textarea[name="show_yoroku_doutoku_manual_text"]').val());
FORM_DATA.append('yoroku_doutoku_manual', JSON.stringify(Object.fromEntries(FORM_DATA_MAP)));
} else if(target == 'shoken_record') {
FORM_DATA_MAP.set('show_yoroku_shoken_manual_type', $('#' + target).find('input:radio[name="show_yoroku_shoken_manual_type"]:checked').val());
FORM_DATA_MAP.set('show_yoroku_shoken_manual_text', $('#' + target).find('textarea[name="show_yoroku_shoken_manual_text"]').val());
let result_array = new Array();
$('#' + target).find('.add_select select[name="show_yoroku_shoken_manual_select"] option:selected').each(function(index, data){
result_array.push($(data).val());
});
let isExisted = validsSameValue(result_array);
if(!isExisted) {
$('#' + target).find('.add_select select[name="show_yoroku_shoken_manual_select"] option:selected').each(function(index, data){
SELECT_ARRAY.push($(data).val());
});
} else {
$(edit).find('.msg p').remove();
$(edit).find('.msg').append('<p>同じ項目が複数選択されています。</p>');
return false;
}
SELECT_ITEM_MAP.set('items', SELECT_ARRAY)
FORM_DATA_MAP.set('show_yoroku_shoken_manual_select', JSON.stringify(Object.fromEntries(SELECT_ITEM_MAP)));
FORM_DATA_MAP.set('show_yoroku_shoken_manual_empty', Number($('#' + target).find('input[name="show_yoroku_shoken_manual_empty"]').prop('checked')));
FORM_DATA.append('yoroku_shoken_manual', JSON.stringify(Object.fromEntries(FORM_DATA_MAP)));
} else if(target == 'attendence_record') {
FORM_DATA_MAP.set('show_yoroku_presence_bikou_hosoku', $('#' + target).find('input:radio[name="show_yoroku_presence_bikou_hosoku"]:checked').val());
// FORM_DATA_MAP.set(); 備考欄に補足集計の文言(追加予定)
FORM_DATA_MAP.set('show_yoroku_presence_under_text', $('#' + target).find('textarea[name="show_yoroku_presence_under_text"]').val());
const OBJ = new Object();
$('#' + target).find('input.show_yoroku_presence_items').each(function(index, data){
OBJ[$(data).attr('id')] = Number($(data).prop('checked'));
});
FORM_DATA_MAP.set('show_yoroku_presence_items', JSON.stringify(OBJ));
FORM_DATA.append('attendence_record_manual', JSON.stringify(Object.fromEntries(FORM_DATA_MAP)));
} else if(target == 'subject_bikou_record') {
FORM_DATA_MAP.set('show_yoroku_subject_bikou_use', $('#' + target).find('input:radio[name="show_yoroku_subject_bikou_use"]:checked').val());
let result_id_array = new Array();
$('#' + target).find('.add_select').each(function(index, data){
result_id_array.push(Number($(data).find('select option:selected').val()));
});
let isExistedId = validsSameValue(result_id_array);
let isExistedText = false;
let result_text_array = new Array();
$('#' + target).find('.add_select').each(function(index, data){
console.log($(data).find('input').val() == '', $(data).find('input').val())
if($(data).find('input').val() == '') {
return isExistedText = true;
}
result_text_array.push($(data).find('input').val());
});
let isExistedSameText = validsSameValue(result_text_array);
if(!isExistedId && !isExistedSameText && !isExistedText) {
const OBJECT = new Object();
$('#' + target).find('.add_select').each(function(index, data){
const OBJ = new Object();
OBJ['sub_subject_id'] = Number($(data).find('select option:selected').val());
OBJ['text'] = $(data).find('input').val();
OBJECT[OBJ['sub_subject_id']] = OBJ;
});
FORM_DATA_MAP.set('show_yoroku_subject_bikou', JSON.stringify(OBJECT));
} else {
$(edit).find('.msg p').remove();
if(isExistedId) {
$(edit).find('.msg').append('<p>同じ科目が複数選択されています。</p>');
}
if(isExistedSameText) {
$(edit).find('.msg').append('<p>同じテキストが入力されています。</p>');
}
console.log("isExistedText", isExistedText)
if(isExistedText) {
$(edit).find('.msg').append('<p>未入力のフォームがあります。</p>');
}
if(isExistedId || isExistedSameText || isExistedText) {
return false;
}
}
FORM_DATA.append('subject_bikou_manual', JSON.stringify(Object.fromEntries(FORM_DATA_MAP)));
}
return FORM_DATA;
}
FORM_DATAを使う場合のオプションは
- contentType: false,
- processData: false ,
通常は
- contentType: ‘application/x-www-form-urlencoded; charset=UTF-8’;
- processData: true ,
Javascript(バニラ)
<script>
(function(){
/**
* 総合的な学習の時間の記録
* new_yoroku_flgが2の場合のcss調整
* */
const GAKUNEN = new Object();
document.querySelectorAll('.sogo_time_record').forEach( (element, index) => {
let text = element.textContent.trim();
GAKUNEN[text] = text;
});
let ajust_style_100 = new Object();
let ajust_style_130 = new Object();
document.querySelectorAll('.sogo_table td').forEach( (element, index) => {
ajust_style_100[index] = false;
ajust_style_130[index] = false;
let gakunen = false
if(index == 0 || index == 1 || index == 2) {
typeof GAKUNEN[1] !== 'undefined' && GAKUNEN[1] == 1 ? gakunen = true : gakunen = false;
} else if(index == 3 || index == 4 || index == 5) {
typeof GAKUNEN[2] !== 'undefined' && GAKUNEN[2] == 2 ? gakunen = true : gakunen = false;
} else if(index == 6 || index == 7 || index == 8) {
typeof GAKUNEN[3] !== 'undefined' && GAKUNEN[3] == 3 ? gakunen = true : gakunen = false;
}
let text = element.textContent.trim();
if(index == 2 || index == 5 || index == 8) {
if(gakunen && text.length > 100) {
ajust_style_100[index] = true;
if(text.length > 130) {
ajust_style_130[index] = true;
}
}
}
});
document.querySelectorAll('.sogo_table td').forEach( (element, index) => {
if(ajust_style_100[index]) {
element.style.lineHeight = '13px';
if(ajust_style_130[index]) {
element.style.fontSize = '11px';
element.style.lineHeight = '13px';
}
}
});
})();
</script>
オブジェクトの中身チェック
typeof GAKUNEN[1] !== ‘undefined’