メルカリの商品出品画面を参考にコピーを作る
参考画面メルカリ
使用する機能
##Haml
new.html.haml
.sell
%header.sell-header
= link_to root_path do
= image_tag 'mercari_top_logo.svg', alt: 'mercari', height: '49', width: '185'
-#メイン部分
%main
%section.sell-container
= form_with model: @item do |f|
-# 画像部分
.sell-container__content
.sell-title
%h3.sell-title__text
出品画像
%span.sell-title__require
必須
.sell-container__content__max-sheet 最大10枚までアップロードできます
.sell-container__content__upload
.sell-container__content__upload__items
.sell-container__content__upload__items__box
%ul#output-box
%div#image-input{tabindex:"0"}
= f.label :images, for: "item_images0", class: 'sell-container__content__upload__items__box__label', data: {label_id: 0 } do
= f.file_field :images, multiple: true, class: "sell-container__content__upload__items__box__input", id: "item_images0", style: 'display: none;'
%pre
%i.fas.fa-camera.fa-lg
ドラッグアンドドロップ
またはクリックしてファイルをアップロード
.error-messages#error-image
-#商品名部分
.sell-container__content
.sell-title
%h3.sell-title__text
商品名
%span.sell-title__require
必須
= f.text_field :name, {class:'sell-container__content__name', required: "required", placeholder: '商品名(必須 40文字まで)'}
.error-messages#error-name
.sell-title
%h3.sell-title__text
商品の説明
%span.sell-title__require
必須
= f.text_area :text,{class: 'sell-container__content__description', required: "required", rows: '7', maxlength: '1000', placeholder: text_placeholder}
-# placeholderでtems_helperを呼び出す
.sell-container__content__word-count
%span#word-count
0
/1000
.error-messages#error-text
-# 詳細部分
.sell-container__content
%h3.sell-sub-head 商品の詳細
.sell-container__content__details
.sell-title
%h3.sell-title__text
カテゴリー
%span.sell-title__require
必須
.sell-collection_select
= f.label :category_id, {class: 'sell-collection_select__label'} do
= f.collection_select :category_id, @category_parent, :id, :name, {prompt: "選択して下さい"},{ class: 'sell-collection_select__input', id: 'category-select', required: "required"}
%i.fas.fa-chevron-down
.error-messages#error-category
.sell-title
%h3.sell-title__text
商品の状態
%span.sell-title__require
必須
.sell-collection_select
= f.label :condition_id, {class: 'sell-collection_select__label'} do
= f.collection_select :condition_id, Condition.all, :id, :condition, {prompt: '選択して下さい'},{ class: 'sell-collection_select__input', id: 'condition-select', required: "required"}
%i.fas.fa-chevron-down
.error-messages#error-condition
-# 配送部分
.sell-container__content
%h3.sell-sub-head
%p 配送について
= link_to '/delivery',target: '_blank',class: 'sell-sub-head__guides-link' do
%i.far.fa-question-circle
.sell-container__content__delivery
.sell-title
%h3.sell-title__text
配送料の負担
%span.sell-title__require
必須
.sell-collection_select
= f.label :deliverycost_id, {class: 'sell-collection_select__label'} do
= f.collection_select :deliverycost_id, Deliverycost.all, :id, :payer, {prompt: '選択して下さい'},{ class: 'sell-collection_select__input', id: 'deliverycost-select', required: "required"}
%i.fas.fa-chevron-down
.error-messages#error-deliverycost
.sell-title
%h3.sell-title__text
発送元の地域
%span.sell-title__require
必須
.sell-collection_select
= f.label :pref_id, class: 'sell-collection_select__label' do
= f.collection_select :pref_id, Pref.all, :id, :name, {prompt: '選択して下さい'},{ class: 'sell-collection_select__input', id: 'pref-select', required: "required"}
%i.fas.fa-chevron-down
.error-messages#error-pref
.sell-title
%h3.sell-title__text
発送までの日数
%span.sell-title__require
必須
.sell-collection_select
= f.label :delivery_days_id, class: 'sell-collection_select__label' do
= f.collection_select :delivery_days_id, DeliveryDays.all, :id, :days, {prompt: '選択して下さい'},{ class: 'sell-collection_select__input', id: 'delivery_days-select', required: "required"}
%i.fas.fa-chevron-down
.error-messages#error-delivery_days
-# 価格部分
.sell-container__content
%h3.sell-sub-head
%p 販売価格(300〜9,999,999)
= link_to '/price',target: '_blank', class: 'sell-sub-head__guides-link' do
%i.far.fa-question-circle
.sell-container__content__price
.sell-title
%h3.sell-title__text
販売価格
%span.sell-title__require
必須
.sell-container__content__price__form
= f.label :price, class: 'sell-container__content__price__form__label' do
¥
= f.number_field :price, {placeholder: '0', value: '', autocomplete:"off", class: 'sell-container__content__price__form__box', required: "required"}
.error-messages#error-price
.sell-container__content__commission
.sell-container__content__commission__left
販売手数料 (10%)
.sell-container__content__commission__right ー
.sell-container__content__profit
.sell-container__content__profit__left
販売利益
.sell-container__content__profit__right ー
.submit-btn
= f.submit '出品する', class: 'submit-btn__sell-btn'
= link_to 'もどる', root_path, class: 'submit-btn__return-btn'
.attention-box
%p
禁止されている
= link_to '行為', '/prohibited_conduct', target: '_blank'
および
= link_to '出品物', '/prohibited_item', target: '_blank'
を必ずご確認ください。
= link_to '偽ブランド品', '/counterfeit_goods', target: '_blank'
や
= link_to '盗品物', '/stolen_goods', target: '_blank'
などの販売は犯罪であり、法律により処罰される可能性があります。また、出品をもちまして
= link_to '加盟店規約', '/seller_terms', target: '_blank'
に同意したことになります。
%footer.sell-footer
%nav
%ul.clearfix
%li
= link_to '#' do
プライバシーポリシー
%li
= link_to '#' do
メルカリ利用規約
%li
= link_to '#' do
特定商取引に関する表記
= link_to root_path, class: 'footer__logo' do
= image_tag 'logo-gray.svg', alt: 'mercari', height: '65', width: '80'
%p
%small
© Mercari, Inc.
CSS
items_new.scss
a {
color: inherit;j[i
text-decoration: none;
box-sizing: border-box;
}
img {
vertical-align: middle;
box-sizing: border-box;
}
.error-messages{
color: #ff0211;
font-size: 14px;
line-height: 1.4em;
margin: 16px 0;
box-sizing: border-box;
}
.sell-title{
align-items: center;
margin: 0!important;
box-sizing: border-box;
&__text{
font-size: 14px;
font-weight: 600;
line-height: 1.4em;
}
&__require{
margin-left: 8px;
font-size: 12px;
padding: 0 4px;
background-color: #ff0211;
color: #fff;
border-radius: 2px;
display: inline-block;
font-style: normal;
font-weight: 600;
line-height: 1.4em;
margin: 0;
}
}
.sell-sub-head{
box-sizing: border-box;
color: rgb(136, 136, 136);
font-size: 14px;
font-weight: 600;
line-height: 1.4em;
margin-bottom: 24px;
display: flex;
&__guides-link{
color: rgb(0, 149, 238);
margin-left: 4px;
}
}
.sell-collection_select{
box-sizing: border-box;
margin-top: 16px;
&__label{
display: inline-block;
position: relative;
width: 100%;
.fas.fa-chevron-down{
box-sizing: border-box;
pointer-events: none;
position: absolute;
right: 16px;
top: 40%;
color: rgb(136, 136, 136);
height: 48px;
}
}
&__input{
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
color: #222;
font-size: 16px;
height: 48px;
line-height: 1;
margin: 0;
outline: none;
padding: 0 56px 0 16px;
width: 100%;
}
}
// ここより上は繰り返し使用するパーツ
.sell {
box-sizing: border-box;
position: relative;
color: rgb(51, 51, 51);
background-color: rgb(245, 245, 245);
font-family: Arial, 游ゴシック体, YuGothic, メイリオ, Meiryo, sans-serif;
font-size: 14px;
line-height: 1;
box-sizing: border-box;
.sell-header{
box-sizing: border-box;
height: 128px;
align-items: center;
display: flex;
justify-content: center;
}
.sell-container{
box-sizing: border-box;
max-width: 700px;
width: 100%;
margin: 0px auto;
background-color: rgb(255, 255, 255);
// 画像部分
&__content{
height: auto;
padding: 40px;
border-bottom: 1px;
border-bottom-color: #efefef;
border-bottom-style: solid;
&__max-sheet{
margin-top: 16px;
}
&__upload{
margin-top: 16px;
display: flex;
flex-wrap: wrap;
&__items{
height: auto;
width: 100%;
&__box{
height: auto;
align-content: center;
align-items: center;
cursor: pointer;
display: flex;
flex-wrap: wrap;
justify-content: center;
position: relative;
border-width: 1px;
#output-box{
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 100%;
height: auto;
.preview-image{
box-sizing: border-box;
height: 150px;
width: 20%;
padding: 0px 4px;
margin-top: 8px;
&__figure{
margin:0 auto;
height: 118px;
background-color: rgb(245, 245, 245);
img{
box-sizing: border-box;
width: 100%;
height: 100%;
object-fit: contain;
}
}
&__button{
border-top-width: 1px;
border-top-color: rgb(204, 204, 204);
border-top-style: solid;
background-color: rgb(245, 245, 245);
justify-content: space-around;
display: flex;
align-items: center;
height: 32px;
color: rgb(0, 149, 238);
}
}
#image-input{
box-sizing: border-box;
height: 100%;
-webkit-flex: 1;
flex: 1;
margin-top: 8px;
.sell-container__content__upload__items__box__label{
box-sizing: border-box;
background-color: rgb(245, 245, 245);
height: 150px;
border-width: 1px;
border-style: dashed;
border-color: rgb(204, 204, 204);
text-align: center;
display: flex;
align-items: center;
justify-content: center;
i{
box-sizing: border-box;
margin-bottom: 8px;
}
}
}
}
}
}
}
}
// 商品名部分
&__content{
&__name{
margin-top: 16px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
height: 48px;
padding: 0 16px;
width: 100%;
}
&__description{
margin-top: 16px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
padding: 16px;
width: 100%;
font-size: 16px;
display: block;
}
&__word-count{
text-align: right;
color: #888;
font-size: 12px;
line-height: 1.4em;
}
}
// 詳細は共通パーツのみ
// 配送は共通パーツのみ
// 価格部分
&__content{
&__price{
-webkit-box-align: center;
align-items: center;
box-sizing: content-box;
display: flex;
height: 46px;
justify-content:space-between;
&form__label{
font-size: 14px;
}
&__form__box{
border: 1px solid #ccc;
border-radius: 4px;
height: 48px;
margin-left: 8px;
padding: 0 16px;
width: 300px;
align-items: center;
display: inline-flex;
text-align: right;
}
}
#error-price{
box-sizing: border-box;
text-align: right;
}
&__commission{
display: flex;
justify-content:space-between;
height: 70px;
padding: 12px 0px;
align-items: center;
border-bottom: 1px;
border-bottom-color: #efefef;
border-bottom-style: solid;
}
&__profit{
display: flex;
justify-content:space-between;
height: 70px;
padding: 12px 0px;
align-items: center;
}
}
.submit-btn{
box-sizing: border-box;
margin: 0 auto;
width: 360px;
margin-bottom: 32px;
&__sell-btn{
background-color: #ea352d;
color: #fff;
margin-bottom: 24px;
width: 100%;
font-size: 17px;
height: 48px;
font-weight: 600;
border-radius: 4px;
}
&__return-btn{
background-color: #ccc;
color: #222;
width: 100%;
font-size: 17px;
height: 48px;
font-weight: 600;
padding: 14px 0;
border-radius: 4px;
display: inline-block;
text-align: center;
}
}
.attention-box{
box-sizing: border-box;
font-size: 12px;
line-height: 1.4em;
word-break: keep-all;
a{
box-sizing: border-box;
color: #0095ee;
}
}
}
.sell-footer{
box-sizing: border-box;
padding: 40px 0px;
text-align: center;
nav{
box-sizing: border-box;
.clearfix{
box-sizing: border-box;
display: inline-block;
font-size: 12px;
display: flex;
justify-content: center;
margin-bottom: 40px;
li{
box-sizing: border-box;
margin: 0px 8px;
}
}
}
}
}
JS
items_new.js
$(document).on('turbolinks:load', function(){
// 画像が選択された時プレビュー表示、inputの親要素のdivをイベント元に指定
$('#image-input').on('change', function(e){
//ファイルオブジェクトを取得する
let files = e.target.files;
$.each(files, function(index, file) {
let reader = new FileReader();
//画像でない場合は処理終了
if(file.type.indexOf("image") < 0){
alert("画像ファイルを指定してください。");
return false;
}
//アップロードした画像を設定する
reader.onload = (function(file){
return function(e){
let imageLength = $('#output-box').children('li').length;
// 表示されているプレビューの数を数える
let labelLength = $("#image-input>label").eq(-1).data('label-id');
// #image-inputの子要素labelの中から最後の要素のカスタムデータidを取得
// プレビュー表示
$('#image-input').before(`<li class="preview-image" id="upload-image${labelLength}" data-image-id="${labelLength}">
<figure class="preview-image__figure">
<img src='${e.target.result}' title='${file.name}' >
</figure>
<div class="preview-image__button">
<a class="preview-image__button__edit">編集</a>
<a class="preview-image__button__delete" data-image-id="${labelLength}">削除</a>
</div>
</li>`);
$("#image-input>label").eq(-1).css('display','none');
// 入力されたlabelを見えなくする
if (imageLength < 9) {
// 表示されているプレビューが9以下なら、新たにinputを生成する
$("#image-input").append(`<label for="item_images${labelLength+1}" class="sell-container__content__upload__items__box__label" data-label-id="${labelLength+1}">
<input multiple="multiple" class="sell-container__content__upload__items__box__input" id="item_images${labelLength+1}" style="display: none;" type="file" name="item[images][]">
<i class="fas fa-camera fa-lg"></i>
</label>`);
};
};
})(file);
reader.readAsDataURL(file);
});
});
//削除ボタンが押された時
$(document).on('click', '.preview-image__button__delete', function(){
let targetImageId = $(this).data('image-id');
// イベント元のカスタムデータ属性の値を取得
$(`#upload-image${targetImageId}`).remove();
//プレビューを削除
$(`[for=item_images${targetImageId}]`).remove();
//削除したプレビューに関連したinputを削除
let imageLength = $('#output-box').children('li').length;
// 表示されているプレビューの数を数える
if (imageLength ==9) {
let labelLength = $("#image-input>label").eq(-1).data('label-id');
// 表示されているプレビューが9なら,#image-inputの子要素labelの中から最後の要素のカスタムデータidを取得
$("#image-input").append(`<label for="item_images${labelLength+1}" class="sell-container__content__upload__items__box__label" data-label-id="${labelLength+1}">
<input multiple="multiple" class="sell-container__content__upload__items__box__input" id="item_images${labelLength+1}" style="display: none;" type="file" name="item[images][]">
<i class="fas fa-camera fa-lg"></i>
</label>`);
};
});
// f.text_areaの文字数カウント
$("textarea").keyup(function(){
let txtcount = $(this).val().length;
$("#word-count").text(txtcount);
});
//販売価格入力時の手数料計算
$('#item_price').keyup(function(){
let price= $(this).val();
if (price >= 300 && price <= 9999999){
let fee = Math.floor(price * 0.1);
// 小数点以下切り捨て
let profit = (price - fee);
$('.sell-container__content__commission__right').text('¥'+fee.toLocaleString());
// 対象要素の文字列書き換える
$('.sell-container__content__profit__right').text('¥'+profit.toLocaleString());
} else{
$('.sell-container__content__commission__right').html('ー');
$('.sell-container__content__profit__right').html('ー');
}
});
$(function(){
// カテゴリーセレクトボックスのオプションを作成
function categoryOption(category){
var optionHtml = `<option value="${category.id}">${category.name}</option>`;
return optionHtml;
}
// 親カテゴリー選択後のイベント
$('#category-select-parent').on('change', function(){
let parentCategoryId = $(this).val();
//選択された親カテゴリーのIDを取得
if (parentCategoryId == ''){
//親カテゴリーが空(初期値)の時
$('#select-children-box').remove();
$('#select-grandchildren-box').remove();
//子と孫を削除するする
}else{
$.ajax({
url: '/items/category_children',
type: 'GET',
data: { parent_id: parentCategoryId },
dataType: 'json'
})
.done(function(category_children){
$('#select-children-box').remove();
$('#select-grandchildren-box').remove();
//親が変更された時、子と孫を削除するする
let optionHtml = '';
category_children.forEach(function(child){
optionHtml += categoryOption(child);
//option要素を作成する
});
$('#error-category').before(`<div class="sell-collection_select " id="select-children-box">
<label class="sell-collection_select__label" for="item_category_id">
<select class="sell-collection_select__input" id="category-select-children" required="required" name="item[category_id]">
<option value="">選択して下さい</option>
${optionHtml}
</select>
<i class="fas fa-chevron-down"></i>
</label>
</div>`
);
})
.fail(function(){
alert('カテゴリー取得に失敗しました');
});
}
});
// 子カテゴリー選択後のイベント
$('.sell-container__content__details').on('change', '#category-select-children', function(){
let childrenCategoryId = $(this).val();
//選択された子カテゴリーのIDを取得
if (childrenCategoryId == ''){
//子カテゴリーが空(初期値)の時
$('#select-grandchildren-box').remove();
//孫以下を削除する
}else{
$.ajax({
url: '/items/category_grandchildren',
type: 'GET',
data: { child_id: childrenCategoryId },
dataType: 'json'
})
.done(function(category_grandchildren){
$('#select-grandchildren-box').remove();
//子が変更された時、孫を削除するする
let optionHtml = '';
category_grandchildren.forEach(function(grandchildren){
optionHtml += categoryOption(grandchildren);
//option要素を作成する
});
$('#error-category').before(`<div class="sell-collection_select " id="select-grandchildren-box">
<label class="sell-collection_select__label" for="item_category_id">
<select class="sell-collection_select__input" id="category-select-grandchildren" required="required" name="item[category_id]">
<option value="">選択して下さい</option>
${optionHtml}
</select>
<i class="fas fa-chevron-down"></i>
</label>
</div>`
);
})
.fail(function(){
alert('カテゴリー取得に失敗しました');
});
}
});
});
// 各フォームの入力チェック
$(function(){
//画像
$('#image-input').on('focus',function(){
$('#error-image').text('');
$('#image-input').on('blur',function(){
$('#error-image').text('');
let imageLength = $('#output-box').children('li').length;
if(imageLength ==''){
$('#error-image').text('画像がありません');
}else if(imageLength >10){
$('#error-image').text('画像を10枚以下にして下さい');
}else{
$('#error-image').text('');
}
});
});
//送信しようとした時
$('form').on('submit',function(){
let imageLength = $('#output-box').children('li').length;
if(imageLength ==''){
$('body, html').animate({ scrollTop: 0 }, 500);
$('#error-image').text('画像がありません');
}else if(imageLength >10){
$('body, html').animate({ scrollTop: 0 }, 500);
$('#error-image').text('画像を10枚以下にして下さい');
}else{
return true;
}
});
//画像を削除した時
$(document).on('click','.preview-image__button__delete',function(){
let imageLength = $('#output-box').children('li').length;
if(imageLength ==''){
$('#error-image').text('画像がありません');
}else if(imageLength >10){
$('#error-image').text('画像を10枚以下にして下さい');
}else{
$('#error-image').text('');
}
});
//商品名
$('.sell-container__content__name').on('blur',function(){
let value = $(this).val();
if(value == ""){
$('#error-name').text('入力してください');
$(this).css('border-color','red');
}else{
$('#error-name').text('');
$(this).css('border-color','rgb(204, 204, 204)');
}
});
//商品説明
$('.sell-container__content__description').on('blur',function(){
let value = $(this).val();
if(value == ""){
$('#error-text').text('入力してください');
$(this).css('border-color','red');
}else{
$('#error-text').text('');
$(this).css('border-color','rgb(204, 204, 204)');
}
});
//カテゴリーのエラーハンドリング
function categoryError(categorySelect){
let value = $(categorySelect).val();
if(value == ""){
$('#error-category').text('選択して下さい');
$(categorySelect).css('border-color','red');
}else{
$('#error-category').text('');
$(categorySelect).css('border-color','rgb(204, 204, 204)');
}
};
//親カテゴリー
$('#category-select-parent').on('blur',function(){
categoryError('#category-select-parent')
});
//子カテゴリー
$('.sell-container__content__details').on('blur', '#category-select-children', function(){
categoryError('#category-select-children')
});
//孫カテゴリー
$('.sell-container__content__details').on('blur', '#category-select-grandchildren', function(){
categoryError('#category-select-grandchildren')
});
//状態
$('#condition-select').on('blur',function(){
let value = $(this).val();
if(value == ""){
$('#error-condition').text('選択して下さい');
$(this).css('border-color','red');
}else{
$('#error-condition').text('');
$(this).css('border-color','rgb(204, 204, 204)');
}
});
//送料負担
$('#deliverycost-select').on('blur',function(){
let value = $(this).val();
if(value == ""){
$('#error-deliverycost').text('選択して下さい');
$(this).css('border-color','red');
}else{
$('#error-deliverycost').text('');
$(this).css('border-color','rgb(204, 204, 204)');
}
});
//発送元
$('#pref-select').on('blur',function(){
let value = $(this).val();
if(value == ""){
$('#error-pref').text('選択して下さい');
$(this).css('border-color','red');
}else{
$('#error-pref').text('');
$(this).css('border-color','rgb(204, 204, 204)');
}
});
//発送までの日数
$('#delivery_days-select').on('blur',function(){
let value = $(this).val();
if(value == ""){
$('#error-delivery_days').text('選択して下さい');
$(this).css('border-color','red');
}else{
$('#error-delivery_days').text('');
$(this).css('border-color','rgb(204, 204, 204)');
}
});
//価格
$('.sell-container__content__price__form__box').on('blur',function(){
let value = $(this).val();
if(value < 300 || value > 9999999){
$('#error-price').text('300以上9999999以下で入力してください');
$(this).css('border-color','red');
}else{
$('#error-price').text('');
$(this).css('border-color','rgb(204, 204, 204)');
}
});
});
});
コントローラー
items.controller.rb
def new
@item = Item.new
@category_parent = Category.where("ancestry is null")
end
# 親カテゴリーが選択された後に動くアクション
def category_children
@category_children = Category.find("#{params[:parent_id]}").children
#親カテゴリーに紐付く子カテゴリーを取得
end
# 子カテゴリーが選択された後に動くアクション
def category_grandchildren
@category_grandchildren = Category.find("#{params[:child_id]}").children
#子カテゴリーに紐付く孫カテゴリーの配列を取得
end
def create
@item = Item.new(item_params)
if @item.save
redirect_to root_path
else
render :new
end
end
private
def item_params
params.require(:item).permit(:name, :text, :category_id, :condition_id, :deliverycost_id, :pref_id, :delivery_days_id, :price, images: []).merge(user_id: current_user.id, boughtflg_id:"1")
end
end
モデル
item.rb
class Item < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to_active_hash :condition
belongs_to_active_hash :pref
belongs_to_active_hash :deliverycost
belongs_to_active_hash :delivery_days
belongs_to_active_hash :boughtflg
# 上記active_hashのアソシエーション
validate :images_presence
validates :name, :text, :category_id, :condition_id, :deliverycost_id, :pref_id, :delivery_days_id, :boughtflg_id,:user_id, presence: true
validates :price, presence: true, numericality: { greater_than_or_equal_to: 300, less_than_or_equal_to: 9999999 }
has_many_attached :images
belongs_to :user, foreign_key: 'user_id'
# optional: true後で消す belongs_toのnotnull制約解放のため使用している
belongs_to :category
#imageのバリデーション
def images_presence
if images.attached?
# inputに保持されているimagesがあるかを確認
if images.length > 10
errors.add(:image, '10枚まで投稿できます')
end
else
errors.add(:image, '画像がありません')
end
end
end
jbuilder
category_children.json.jbuilder
json.array! @category_children do |child|
json.id child.id
json.name child.name
end
category_grandchildren.json.jbuilder
json.array! @category_grandchildren do |grandchild|
json.id grandchild.id
json.name grandchild.name
end
機能の実装
長いですね、すみません
冒頭の3つ機能についてはリンクで飛んで下さい。