jade ストライプテーブルJavaScript

ストライプテーブルJavaScript

style.css
@use postcss-cssnext;

th, td {
  padding: 1em;
}

th {
  background: #222;
  color: white;
}

tr {
  &:nth-child(even) {
    background: #f2f2f2;
  }
  
  &:nth-child(odd) {
    background: #ccc;
  }
}

.hover {
  background: #aaf!important;
}
.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
script.js
const $rows = document.querySelector('tbody').rows;
const $td = document.querySelectorAll('tbody td');

const addClass = (el) => {
  el.classList.add('hover');
}

const removeClass = (el) => {
  el.classList.remove('hover');
}

Array.from($rows).forEach(function(el) {
  el.addEventListener('mouseover', function() {
    addClass(this);
  }, false);
  el.addEventListener('mouseout', function() {
    removeClass(this);
  }, false);
});

Array.from($td).forEach(function(el) {
  el.addEventListener('mouseover', function() {
    for (var 1=0; i<$rows.length; i++) {
      addClass($rows[i].cells[this.cellIndex]);
    }
  }, false);
  el.addEventListener('mouseout', function() {
    for (var 1=0; i<$rows.length; i++) {
      removeClass($rows[i].cells[this.cellIndex]);
    }
  }, false);
});
javascript.markdown
ストライプテーブル JavaScript
--------------------


A [Pen](https://codepen.io/taquaki/pen/pWGPwV) by [Takaaki Sato](https://codepen.io/taquaki) on [CodePen](https://codepen.io).

[License](https://codepen.io/taquaki/pen/pWGPwV/license).
index.pug
table#test
  thead
    tr
      th no
      th name
      th content
  tbody
    - for (var i=1; i<=5; i++)
      tr
        td #{i}
        td name #{i}
        td content #{i}

jade SPAサンプル#1

SPAサンプル#1

style.css
body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: #777;
}

#spa {
  postition: absolute;
  top: 8px;
  left: 8px;
  bottom: 8px;
  right: 8px;
  border-radius: 8px 8px 0 8px;
  background-color: #fff;
}

.spa-slider {
  position: absolute;
  bottom: 0;
  right: 2px;
  width: 300px;
  height: 16px;
  cursor: pointer;
  border-radius: 8px 0 0 0;
  background-color: #f00;
}
spa-1.markdown
SPAサンプル #1
----------


A [Pen](https://codepen.io/taquaki/pen/wqgyLv) by [Takaaki Sato](https://codepen.io/taquaki) on [CodePen](https://codepen.io).

[License](https://codepen.io/taquaki/pen/wqgyLv/license).
script.js
// モジュール/spa/
// チャットスライダー機能を提供する
const spa = (() =>{
  // モジュールスコープ変数
  // 定数を設定する
  const CONFIG_MAP = {
    EXTENDED_HEIGHT: 434,
    EXTENDED_TITLE: 'Click to retract',
    RETRACTED_HEIGHT: 16,
    RETRACTED_TITLE: 'Click to extend',
    TEMPLATE_HTML: '<div class="spa-slider"><\/div>',
    DURATOIN: 150
  }

  // その他のすべてのモジュールスコープ変数を宣言する
  let $chatSlider, toggleSlider, onClickSlider, initModule

  // DOMメソッド/toggleSlider/
  // スライダーの高さを切り替える
  toggleSlider = () => {
    const sliderHeight = $chatSlider.clientHeight
    if (sliderHeight === CONFIG_MAP.RETRACTED_HEIGHT) {
      $chatSlider.animate([
        {
          height: CONFIG_MAP.RETRACTED_HEIGHT + 'px'
        },
        {
          height: CONFIG_MAP.EXTENDED_HEIGHT + 'px'
        }
      ],{
        duration: CONFIG_MAP.DURATOIN,
        easing: 'linear',
        fill: 'forwards'
      })
      $chatSlider.setAttribute('title', CONFIG_MAP.EXTENDED_TITLE)
      return true
    } else if (sliderHeight === CONFIG_MAP.EXTENDED_HEIGHT) {
      $chatSlider.animate([
        {
          height: CONFIG_MAP.EXTENDED_HEIGHT + 'px'
        },
        {
          height: CONFIG_MAP.RETRACTED_HEIGHT + 'px'
        }
      ],{
        duration: CONFIG_MAP.DURATOIN,
        easing: 'linear',
        fill: 'forwards'
      })
      $chatSlider.setAttribute('title', CONFIG_MAP.RETRACTED_TITLE)
      return true
    }

    return false
  }

  // イベントハンドラ/onClickSlider/
  // クリックイベントを受け取り、toggleSliderを呼び出す
  onClickSlider = (event) => {
    toggleSlider()
    return false
  }

  // パブリックメソッド/initModule/
  // 初期状態を設定し、機能を提供する
  initModule = function ($container) {
    // HTMLをレンダリングする
    $container.innerHTML = CONFIG_MAP.TEMPLATE_HTML
    $chatSlider = $container.querySelector('.spa-slider')
    // スライダーの高さとタイトルを初期化する
    // ユーザクリックイベントをイベントハンドラにバインドする
    $chatSlider.setAttribute('title', CONFIG_MAP.RETRACTED_TITLE)
    $chatSlider.addEventListener('click', onClickSlider)
    return true
  }

  return { initModule: initModule }
})();

// DOMの準備ができたらspaを開始する
document.addEventListener('DOMContentLoaded', () => {
  spa.initModule(document.getElementById('spa'))
})
index.pug
doctype html
html
  head
    title SPA #1
body
  #spa  

jade 画布で円上のÑ分点を结ぶ

JS-帆布で円上のÑ分点を结ぶ

script.babel
function drawPolygon(ctx, n, x, y, r, color) {
  ctx.strokeStyle = color;
  ctx.beginPath();
  for(let i=0; i<n; i++) {
    const t = i*2*Math.PI/n;
    for(var j=i+1; j<n; j++) {
      const s = j*2*Math.PI/n;
      ctx.moveTo(x+r*Math.cos(t), y+r*Math.sin(t));
      ctx.lineTo(x+r*Math.cos(s), y+r*Math.sin(s));
    }
  }
  ctx.stroke();
}

window.onload = function() {
  const canvas = document.getElementById('mycanvas');
  const ctx = canvas.getContext('2d');
  drawPolygon(ctx, 6, 170, 170, 150, 'darkblue');
  drawPolygon(ctx, 12, 490, 170, 150, 'darkblue');
  drawPolygon(ctx, 18, 810, 170, 150, 'darkblue');
}
js-canvas-n.markdown
JS-Canvasで円上のN分点を結ぶ
-------------------


A [Pen](https://codepen.io/taquaki/pen/RjxZYg) by [Takaaki Sato](https://codepen.io/taquaki) on [CodePen](https://codepen.io).

[License](https://codepen.io/taquaki/pen/RjxZYg/license).
index.pug
canvas#mycanvas(width='1000' height='400')

jade [Google Map]使用自定义标记和内部块#js #map的包装器的渐进增强谷歌地图

[Google Map]使用自定义标记和内部块#js #map的包装器的渐进增强谷歌地图

map.pug
.footer__map.map
    .map__container
        div(id="js-map")
        noscript
            img.map__noscript-map(src="./images/map.jpg" alt="Карта")
    .map__wrapper
map.less
@color-img-alt: grey;
@layout-size: 880px;

.wrapper() {
  width: @layout-size;
  margin: 0 auto;
}

.map {
  &__container {
    position: absolute;
    z-index: 1;
    width: 100%;
    height: 100%;
  }

  &__noscript-map {
    display: block;
    margin: auto;
    width: 1200px;
    height: 100%;
    color: @color-img-alt;
  }

  &__wrapper {
    position: relative;
    z-index: 2;

    .wrapper();
  }
}
map.js.pug
script.
    div = document.getElementById("js-map");

    // This is for avoid the bug in Safari
    div.setAttribute("style", "height: 26rem; width: 100%;");

    function initMap() {
        var coordinates = { lat: 45.043675, lng: 38.941264 },   
        markerCoordinates = { lat: 45.043391, lng: 38.944560 }, 
        markerImage = "images/map-marker.png",
        zoom = 18,
        map = new google.maps.Map(document.getElementById("js-map"), {
            center: coordinates,
            zoom: zoom,
            scrollwheel: false,
            disableDefaultUI: true
        }),
        marker = new google.maps.Marker({
            position: markerCoordinates,
            map: map,
            icon: markerImage
        });
    }

script(async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap")

jade [响应图像mixin] #pug #mixin

[响应图像mixin] #pug #mixin

respimage.pug
//- Input

mixin respImage
    - var src1 = src.replace(/(\.[\w\d_-]+)$/i, '@medium$1') + ' ' + size + 'w'
    - var src2 = src.replace(/(\.[\w\d_-]+)$/i, '@large$1') + ' ' + size * 1.5 + 'w'
    - var src3 = src.replace(/(\.[\w\d_-]+)$/i, '@huge$1') + ' ' + size * 2 + 'w'

    img(class  = imgClass
        srcset = src1 + ', ' + src2 + ', ' + src3
        sizes  = sizes
        src    = src.replace(/(\.[\w\d_-]+)$/i, '@medium$1')
        alt    = alt)

- src      = 'images/picture.png'
- imgClass = 'section__image'
- size     = 300
- sizes    = size / 16 + 'em'
- alt      = 'Image description'

+respImage

//- Output

<img class="section__image" srcset="images/picture@medium.png 300w, images/picture@large.png 450w, images/picture@huge.png 600w" sizes="18.75em" src="images/picture@medium.png" alt="Image description"/>

jade 帕格每个+单个其他类

Вциклевывестиодинэлементсдругимклассом$ b $bАктивныйэлементменюилитаба

index.pug
-
		var items = [
			'Сеть заведений',
			'Ресторана',
			'Фастфуда',
			'Бара',
			'Кафе',
			'Кофейни',
			'Столовой',
			'Пиццерии',
			'Доставки'
		];
	each item in items
		.solution__head-el.tab-head(class=item== 'Сеть заведений' ? 'tab-head--active' : null) #{item}

jade スマホナビ(ハンバーガー+プルダウン)

header.js
(function() {
  let $menuTrigger = document.getElementById('menuTrigger');
  let $headerNav = document.getElementById('headerNav');
  let $headerNav_list = document.getElementById('headerNav_list');
  let headerNav_aArray = document.querySelectorAll('#headerNav_list a');
  let $headerLogo = document.getElementById('headerLogo');
  $menuTrigger.addEventListener('click', function() {
    let $target = this;
    let windowHeight = window.outerHeight;
    let headerHeight = document.getElementById('header').clientHeight;
    let maxHeight = windowHeight - headerHeight;
    $target.classList.toggle('is-active');
    $headerNav.classList.toggle('is-active');
    $headerNav_list.classList.toggle('is-active');
    if ($headerNav.classList.contains('is-active')) {
      let listHeight = $headerNav_list.clientHeight;
      console.log(listHeight, maxHeight);
      if (listHeight >= maxHeight) {
        $headerNav.style.height = maxHeight + 'px';
      } else {
        $headerNav.style.height = listHeight + 'px';
      }
    } else {
      $headerNav.style.height = 0;
    }
  });
  function closeMenu() {
    $menuTrigger.classList.remove('is-active');
    $headerNav.classList.remove('is-active');
    $headerNav_list.classList.remove('is-active');
    $headerNav.style.height = 0;
  }
}());
header.scss
.header {
  position: fixed;
  z-index: 10000;
  top: 0;
  display: flex;
  width: 100%;
  background: #000;
  justify-content: center;
  align-items: center;
  @media (--desktop) {
    font-size: 21px;
  }
  a {
    color: #fff;
    text-decoration: none;
  }
  &,
  .container,
  .headerLogo,
  .headerNav_list a {
    @media (--desktop) {
    }
    height: 65px;
  }
  .container {
    line-height: 1;
    @media screen and (--tabletMax) {
      /* position: relative; */
      z-index: 10000;
      width: 100%;
      padding: 0;
      background: #000;
    }
    @media (--desktop) {
      position: absolute;
      top: 50%;
      left: 50%;
      margin: 0;
      transform: translate(-50%, -50%);
    }
    @media (--desktop) {
      width: 900px;
      padding: 0;
    }
  }
  .headerLogo {
    display: table;
    float: left;
    width: 200px;
    margin: 0;
    @media screen and (--tabletMax) {
      @util margin(null null null 4%);
      width: 40%;
    }
    a {
      display: table-cell;
      vertical-align: middle;
    }
  }
  .headerActionArea {
    display: table;
    float: right;
    @media (--desktop) {
      display: none;
    }
  }
  .menuTriggerBox {
    display: table-cell;
    margin: 0;
    vertical-align: middle;
  }
  .menuTriggerBox {
    padding: 16px 13px;
  }
  .menuTrigger {
    position: relative;
    width: 45px;
    height: 32px;
    margin: 0;
    &,
    span {
      box-sizing: border-box;
      transition: all .4s;
    }
    span {
      position: absolute;
      left: 0;
      display: inline-block;
      width: 100%;
      height: 4px;
      border-radius: 4px;
      background-color: #fff;
      &:nth-of-type(1) {
        top: 0;
        animation: menu-bar01 .5s forwards;
      }
      &:nth-of-type(2) {
        top: 14px;
        opacity: 1;
        transition: all .25s .25s;
      }
      &:nth-of-type(3) {
        bottom: 0;
        animation: menu-bar02 .5s forwards;
      }
    }
    &.is-active {
      span {
        &:nth-of-type(1) {
          animation: active-menu-bar01 .5s forwards;
        }
        &:nth-of-type(2) {
          opacity: 0;
        }
        &:nth-of-type(3) {
          animation: active-menu-bar03 .5s forwards;
        }
      }
    }
  }
  .headerNav {
    @media screen and (--tabletMax) {
      overflow: scroll;
      position: absolute;
      z-index: 5000;
      /* top: -100%; */
      /* top: -120vw; */
      top: 65px;
      left: 0;
      width: 100vw;
      height: 0;
      transition: all .4s ease-in-out;
    }
    @media (--desktop) {
      display: table-cell;
      float: right;
      vertical-align: middle;
    }
    &.is-active {
      top: 64px;
      height: 100%;
    }
  }
  .headerNav_list {
    margin: 0;
    padding: 0;
    font-family: "agencyr";
    @media screen and (--tabletMax) {
      background: #de3a38;
      transition: all .4s ease-in-out;
    }
    @media (--desktop) {
      display: table;
    }
    li {
      @media screen and (--tabletMax) {
        text-align: center;
        & + li {
          border-top: 1px solid var(--BLACK);
        }
      }
      @media (--desktop) {
        display: table-cell;
      }
      &.is-entry a {
        @media (--desktop) {
          font-size: 29px;
          color: #ffe326;
        }
      }
    }
    a {
      @media screen and (--tabletMax) {
        display: block;
        height: auto;
        padding: 4%;
        font-size: 24px;
      }
      @media (--desktop) {
        display: table-cell;
        padding-top: 4px;
        padding-right: 16px;
        padding-left: 16px;
        vertical-align: middle;
      }
      &.is-active {
        color: #ffab51;
      }
      &.is-disable {
        color: var(--GRAY_80);
        pointer-events: none;
      }
    }
  }
}

@keyframes menu-bar01 {
  0% {
    transform: translateY(20px) rotate(45deg);
  }
  50% {
    transform: translateY(20px) rotate(0);
  }
  100% {
    transform: translateY(0) rotate(0);
  }
}
@keyframes menu-bar02 {
  0% {
    transform: translateY(-20px) rotate(-45deg);
  }
  50% {
    transform: translateY(-20px) rotate(0);
  }
  100% {
    transform: translateY(0) rotate(0);
  }
}
@keyframes active-menu-bar01 {
  0% {
    transform: translateY(0) rotate(0);
  }
  50% {
    transform: translateY(14px) rotate(0);
  }
  100% {
    transform: translateY(14px) rotate(45deg);
  }
}
@keyframes active-menu-bar03 {
  0% {
    transform: translateY(0) rotate(0);
  }
  50% {
    transform: translateY(-15px) rotate(0);
  }
  100% {
    transform: translateY(-15px) rotate(-45deg);
  }
}
header.pug
header.header#header
  .container#headerContainer
    p#headerLogo.headerLogo
      a(href='index.html' class="invalidLink"): img(src="img/header/header_logo.png" alt="")
    .headerActionArea
      .menuTriggerBox
        p#menuTrigger.menuTrigger
          span
          span
          span
    nav#headerNav.headerNav
      ul#headerNav_list.headerNav_list
        li: a(href='#') link
        li: a.is-disable(href='#') link
        li: a(href='#') link
        li: a(href='#') link
        li: a(href='#') link
        li: a(href='#') link

jade 登录表单

呈现登录表单的模块

actions.pug
.login-form__actions
	.login-form__select
		select#lang_select.login-form__select
			each item, key in options.countryList
				option(value=key selected=(lang === key ? true : false))= item
		i.login-form__arrow.fa.fa-caret-down
	//- a.login-form__act-btn(href='index.php/restore' data-name='restore')= options.text.forgot
	if options.text.email
		a.login-form__act-btn(href='mailto:' + options.text.email)= options.text.contactAdmin
index.js
export default class LoginForm {
	constructor(options, state) {
		this._elem;
		this.options = options;
		this.form = require('./form.pug')({ options, lang: this._getCookie('langCountry'), restore: state });
		this.restoreHandler = this._restoreHandler.bind(this);
	}

	_getCookie(name) {
		  var matches = document.cookie.match(new RegExp(
		    "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
		  ));
		  return matches ? decodeURIComponent(matches[1]) : undefined;
	}	

	_render() {
		this._elem = document.createElement('div');
		this._elem.innerHTML = this.form;
		this._elem = this._elem.firstElementChild;

		// add events
		this._eventsLogin('add');

		window.addEventListener('popstate', e => {
			if(e.state) {
				this.renderState();
				// remove events
				this._eventsLogin();
			}
			else {
				this.renderState('login');
				// add events
				this._eventsLogin('add');				
			}
		});
	}

	_changeLang(e) {
		window.location.href = '?langCountry=' + e.target.value;
	}

	_restoreHandler(e) {
		// remove events
		this._eventsLogin();
		history.pushState('restore', '', e.target.getAttribute('href'));

		// render new state
		this.renderState();

		e.preventDefault();
	}

	_eventsLogin(action = 'remove') {
		// events
		let langEl = this._elem.querySelector('#lang_select');
		let restoreEl = this._elem.querySelector('[data-name="restore"]');

		if(action === 'add') {
			if(langEl) langEl.addEventListener('change', this._changeLang);
			if(restoreEl) restoreEl.addEventListener('click', this.restoreHandler);
		}
		else {
			if(langEl) langEl.removeEventListener('change', this._changeLang);
			if(restoreEl) restoreEl.removeEventListener('click', this.restoreHandler);			
		}
	}

	renderState(state = 'restore') {
		const options = this.options;
		const actions = this._elem.querySelector('.login-form__actions');
		const wrapper = this._elem.querySelector('.login-form__wrapper');
		const body = this._elem.querySelector('.login-form__body');
		let template = state;

		if(state === 'login') {
			this._eventsLogin('add'); //add events
			wrapper.classList.remove('login-form__wrapper--restore');
			body.classList.remove('login-form__body--restore');
			wrapper.parentNode.insertAdjacentHTML('beforeEnd', require('./actions.pug')({ options }));
		}
		else {
			this._eventsLogin(); // remove events
			wrapper.classList.add('login-form__wrapper--restore');
			body.classList.add('login-form__body--restore');
			if(actions) actions.parentNode.removeChild(actions);
		}

		this._elem.querySelector('.login-form__body').innerHTML = require('./'+ state +'.pug')({ options });
	}	

	getElem() {
		this._render();
		return this._elem;
	}
};
use.js
import LoginForm from './modules/LoginForm';

const pathname = location.pathname.split('/');
const loginForm = new LoginForm(settings, history.state || pathname[pathname.length - 1]);

document.addEventListener('DOMContentLoaded', () => {
	document.body.appendChild(loginForm.getElem());
});
restore.pug
input.login-form__input(
	type='email' 
	name='email' 
	required 
	placeholder='E-mail'
)
button.login-form__button= options.text.resetPassword
mixins.pug
mixin error(value)
	.login-form__error.color-red.fz-14.text-center= value
login.pug
input.login-form__input(
	type='text' 
	name='login_name' 
	required 
	placeholder= options.text.login
)
input.login-form__input(
	type='password' 
	name='login_pass' 
	required 
	placeholder= options.text.password
)
if options.errors.captcha
	.g-recaptcha(data-sitekey="6Leg-QcTAAAAAPBs1uRjzOqxNa8z-mJlynOcKEWF")

if options.errors.accessDenied
	+error(options.errors.accessDenied)

if options.errors.captchaMessage
	+error(options.errors.captchaMessage)

if options.errors.bruteforce
	+error(options.errors.bruteforce)

.text-center
	button.login-form__button= options.text.login
form.pug
include ./mixins

.login-form
	form(method='post' action='')
		if options.accountChanged
			#enter !{options.text.accountChanged}
		input(type='hidden' name='login' value='1')
		input(name='submit' type='hidden' value='Login')
		.login-form__wrapper(class=(restore ? 'login-form__wrapper--restore': false))
			.login-form__caption(class=(restore ? 'w-100': false))
				span.login-form__logo
				span.dib.dib--middle.bold= options.text.title

			.login-form__body(class=(restore ? 'login-form__body--restore': false))
				if restore
					include ./restore
				else
					include ./login
		if !restore
			include ./actions

jade post.jade

post.jade
extends base
from mixins.post import make_post

block title
    title= post.title

block page_class
    | page-post-detail

block content
    .posts-expand#posts
        +make_post(post, is_index=False)
        .post-spread
	p 下一篇文章:
    	a(href=posts.previous_one.url)= posts.previous_one.title
	p 上一篇文章:
    	a(href=posts.next_one.url)= posts.next_one.title
	+post.comments_as_html()

jade SVG使用Sprite混音

_svg.pug
mixin svg(id, viewbox, className)
	if viewbox
		svg(viewbox='#{viewbox}', class!=className)
			use(xlink:href='assets/img/sprite.svg#' + id)
	else
		svg(viewbox='0 0 50 50', class!=className)
			use(xlink:href='assets/img/sprite.svg#' + id)

//- Usage: +svg('symbolID', 'viewbox parameters', 'classnames')