javascript Issue84 - 20190620电话导线改善
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript Issue84 - 20190620电话导线改善相关的知识,希望对你有一定的参考价值。
import get from 'lodash/get'
const pageId = document.body.getAttribute('data-pageid')
// トップページかどうか
export const isTopPage = pageId === 'top'
// コース一覧かどうか
export const isReservePage = pageId === 'reserve'
// ポイントON/OFF
/* eslint no-magic-numbers: 0 */
const pointFlg = get(window, 'window.sc_plan_data.plan_existence3')
export const isPointOn = pointFlg === '1' || pointFlg === '3'
// トップページ: 吹き出しが`-cp-target`であればキャンペーン対象
export const isCampaignTop = isTopPage && document.querySelector('.button__balloon.-cp-target') !== null
// コース一覧: キャンペーンバナーがあればキャンペーン対象
export const isCampaignPlanList = isReservePage && document.querySelector('.cp-modal-trigger') !== null
// 第一引数で渡すセレクターに第二引数で渡す文字列を含む要素があれば返却
// jQueryの :contains() 代替
export const contains = (selector, text) => {
const elms = document.querySelectorAll(selector)
return Array.prototype.filter.call(elms, elm => RegExp(text).test(elm.textContent))
}
function callbackFn(activate, options) {
var utils = window.optimizely.get('utils');
var $ = window.optimizely.get('jquery');
utils.waitForElement('.headerCV .ic-plan-cal').then(function() {
var target = $('.headerCV .ic-plan-cal');
var cancelPolling = utils.poll(function() {
var telNum = window.GNAVI.ShopData.tel;
var isPPC = /^050/.test(telNum);
if (target.length > 0 && isPPC) {
cancelPolling();
activate();
} else {
cancelPolling();
}
}, 100);
});
utils.waitForElement('.planCal form').then(function() {
var target = $('.planCal');
var cancelPolling = utils.poll(function() {
var telNum = window.GNAVI.ShopData.tel;
var isPPC = /^050/.test(telNum);
if (target.length > 0 && isPPC) {
cancelPolling();
activate();
} else {
cancelPolling();
}
}, 100);
});
}
import { contains } from '../utils'
const changeReserveBtnTxt = (btn, cls, txtElm, txt) => {
const map = {
pt1: '電話・ネット予約',
pt2: '予約する',
pt3: '空席確認・予約',
}
btn.classList.add(cls)
/* eslint no-param-reassign: 0 */
txtElm.textContent = map[txt]
}
const reserveBtn = txt => {
const ISSUE_CLASS = 'issue84'
// headerCV
const headerCV = document.querySelector('.headerCV')
// コース一覧では何もしない
if (!headerCV) return
headerCV.classList.add(ISSUE_CLASS)
// footerCV
const footerCV = document.querySelector('.footerShopCV')
footerCV.classList.add(ISSUE_CLASS)
// 追従エリア
const stickyReserveWrap = document.getElementById('js-reserve-btn')
const stickyReserveBtnWrap = stickyReserveWrap.querySelector('.ic-net-yoyaku').parentNode.parentNode
stickyReserveWrap.classList.add(ISSUE_CLASS)
stickyReserveBtnWrap.classList.remove('col__12', 'col__9', '-pl8')
// 各ボタンの文言変更
if (txt) {
const headerReserveTxt = contains('.headerCV span', 'ネット予約する')[0]
const footerReserveTxt = contains('.footerShopCV span', 'ネット予約する')[0]
const stickyReserveTxt = contains('#js-reserve-btn span', 'ネット予約する')[0]
const headerReserveBtn = headerCV.querySelector('.ic-net-yoyaku')
const footerReserveBtn = footerCV.querySelector('.hf-ic-net-yoyaku')
const stickyReserveBtn = stickyReserveWrap.querySelector('.ic-net-yoyaku')
const NO_ICON_CLASS = '-no-icon'
changeReserveBtnTxt(headerReserveBtn, NO_ICON_CLASS, headerReserveTxt, txt)
changeReserveBtnTxt(footerReserveBtn, NO_ICON_CLASS, footerReserveTxt, txt)
changeReserveBtnTxt(stickyReserveBtn, NO_ICON_CLASS, stickyReserveTxt, txt)
}
// 追従エリアに電話するボタンがある場合は削除
const telStickyBtn = stickyReserveWrap.querySelector('.ic-tel-sticky')
if (!telStickyBtn) return
const telStickyBtnWrap = stickyReserveWrap.querySelector('.ic-tel-sticky').parentNode.parentNode
telStickyBtnWrap.parentNode.removeChild(telStickyBtnWrap)
}
export default reserveBtn
const pointUseModal = () => {
const pointUseModalTrigger = document.getElementById('js-pointUseModal')
if (!pointUseModalTrigger) return
pointUseModalTrigger.addEventListener('click', () => {
const pointUseModalBtn = document.querySelector('.new-point-use-modal__reserve-button a')
const pointUseModalBtnHref = pointUseModalBtn.getAttribute('href').replace(/#.*$/, '')
// モーダル内の既存のボタンが「ネット予約する」ボタンのhrefをコピーしているので今回追加したハッシュがここでも付いてしまう
// 不要なので削除
pointUseModalBtn.setAttribute('href', pointUseModalBtnHref)
})
}
export default pointUseModal
import forEach from 'lodash/forEach'
import get from 'lodash/get'
import Path from '../../../utils/path'
import { isTopPage, isPointOn, isCampaignTop, isCampaignPlanList } from '../utils'
const MODAL_CLOSE_TRIGGER = 'js-point-tel-modal-close-trg'
// GNAVI変数から環境ごとの画像パスをゲット
const modalImgRootPath = new Path('sp').get('image')
const telNumber = get(window, 'window.GNAVI.ShopData.tel')
const modalBtnTxt = isTopPage ? 'コース一覧・予約' : 'ネット予約コースを見る'
const setPointImgName = () => {
let pointImgName = 'modal_point'
if (!isPointOn) {
// ポイントOFF
pointImgName = `${pointImgName}_off`
} else if (isCampaignTop || isCampaignPlanList) {
// ポイントON & キャンペーン対象
const campaignBnr = document.querySelector('.cp-modal-trigger img')
if (!campaignBnr) return true
const campaignBnrSrc = campaignBnr.getAttribute('src')
// 2倍 | 3倍
if (campaignBnrSrc.match('x2')) {
pointImgName = `${pointImgName}_x2`
} else {
pointImgName = `${pointImgName}_x3`
}
} else {
// ポイントON & キャンペーン対象外
pointImgName = `${pointImgName}_x0`
}
return pointImgName
}
const modalStrDOM = `
<div class="point-tel-modal ${MODAL_CLOSE_TRIGGER}">
<div class="point-tel-modal__inner">
<div class="point-tel-modal__body">
<p><img src="${modalImgRootPath}img/issue84/${setPointImgName()}.png" alt=""></p>
<button type="button" class="point-tel-modal__btn -base">${modalBtnTxt}</button>
</div>
<div class="point-tel-modal__body -tel">
<p>電話予約の場合、ポイントは貯まりません。</p>
<a href="tel:${telNumber}" class="issue84__tel-btn -clr-link" onclick="sc_count('r-smp_plan-reserve-modal_phone_to')">${telNumber}</a>
</div>
<button type="button" class="point-tel-modal__btn -close-text ${MODAL_CLOSE_TRIGGER}" onclick="sc_count('r-smp_plan-reserve-modal_close')">閉じる</button>
<button type="button" class="point-tel-modal__btn -close-icon ${MODAL_CLOSE_TRIGGER}" onclick="sc_count('r-smp_plan-reserve-modal_close')"></button>
</div>
</div>
`
const pointTelModal = () => {
const modalContainer = document.createElement('div')
const page = document.getElementById('page')
const offsetTop = window.pageYOffset
// ページを固定してモーダル下がスクロールしないように
page.setAttribute('style', `position: fixed; z-index: 10; top: -${offsetTop}px; left: 0; width: 100%;`)
page.appendChild(modalContainer)
modalContainer.setAttribute('class', 'point-tel-modal-container')
modalContainer.innerHTML = modalStrDOM
const closeTriggers = document.getElementsByClassName(MODAL_CLOSE_TRIGGER)
const modalBodys = document.querySelectorAll('.point-tel-modal__body')
const pointTelModalInner = document.querySelector('.point-tel-modal__inner')
const pointTelModalBtn = document.querySelector('.point-tel-modal__btn.-base')
// 計測
pointTelModalBtn.addEventListener('click', () => {
window.sc_count('r-smp_plan-reserve-modal_planbtn')
})
// トップページとコース一覧でボタンの役割が変わる
if (isTopPage) {
// モーダル内のボタンにハッシュは不要なので削除
const reserveBtnHref = document
.querySelector('.js-plan-reserve-button')
.getAttribute('href')
.replace(/#.*$/, '')
pointTelModalBtn.setAttribute('onclick', `location.href='${reserveBtnHref}'`)
} else {
pointTelModalBtn.classList.add(MODAL_CLOSE_TRIGGER)
}
if (!isPointOn) pointTelModalInner.classList.add('-point-off')
// モーダルの本体(背景が白い部分)をタップしてもモーダルは閉じないように
forEach(modalBodys, modalBody => {
modalBody.addEventListener('click', ev => {
ev.stopPropagation()
})
})
forEach(closeTriggers, closeTrigger => {
closeTrigger.addEventListener('click', ev => {
ev.stopPropagation()
// ページ固定のstyleを削除
page.removeAttribute('style')
// styleがリムーブされるとscrollが0に戻るため元の位置に戻す
scroll(0, offsetTop)
modalContainer.parentNode.removeChild(modalContainer)
})
})
}
export default pointTelModal
import pointTelModal from '../pointTelModal'
const planList = () => {
const hash = window.location.hash.replace('#', '')
const isHash = () => /rs-smp_plan_list_0[4-6]/.test(hash)
// 規定のハッシュ以外でコース一覧に来ても何もしない
if (!isHash()) return
// ブラウザバックで戻った時はモーダルが起動しないようにハッシュを変更
location.hash = '_'
pointTelModal()
}
export default planList
const headerCV = document.querySelector('.headerCV')
const footerCV = document.querySelector('.footerShopCV')
const attachPopover = () => {
// 以下 /src/alljs/popover.js より
/* global someFunction GNAVI:true */
new GNAVI.Dialog.Popover({
button: '.js-popover-button',
targetId: '#popover_calendar',
})
}
export const replaceWaitingBtn = () => {
const waitingBtn = document.getElementById('js-waiting-service-button')
if (!waitingBtn) return
const headerPlanCalBtnWrap = headerCV.querySelector('.ic-plan-cal').parentNode.parentNode.parentNode.parentNode
if (headerPlanCalBtnWrap.parentNode) {
headerPlanCalBtnWrap.parentNode.insertBefore(waitingBtn, headerPlanCalBtnWrap.nextSibling)
}
}
export const planCalBtn = flg => {
// コース一覧では何もしない
if (!headerCV) return
const headerReserveBtn = headerCV.querySelector('.ic-net-yoyaku').parentNode
const headerplanCalBtn = headerCV.querySelector('.ic-plan-cal').parentNode.parentNode.parentNode.parentNode
const footerReserveBtn = footerCV.querySelector('.hf-ic-net-yoyaku').parentNode
const footerplanCalBtn = footerCV.querySelector('.hf-ic-plan-cal').parentNode.parentNode.parentNode.parentNode
const reserveBtnStickyWrap = document.getElementById('js-reserve-btn')
const reserveStickyBtn = reserveBtnStickyWrap.querySelector('.ic-net-yoyaku').parentNode
// 空席通知ボタンがあったら空席確認ボタン右に移動(ページ上部のCVエリアのみ)
replaceWaitingBtn()
// 空席確認ボタン削除
headerplanCalBtn.parentNode.removeChild(headerplanCalBtn)
footerplanCalBtn.parentNode.removeChild(footerplanCalBtn)
// 追加で空席確認モーダルのイベントをアタッチ
if (flg === 'attach') {
headerReserveBtn.classList.add('js-popover-button')
footerReserveBtn.classList.add('js-popover-button')
reserveStickyBtn.classList.add('js-popover-button')
attachPopover()
// onclickの値も変更
headerReserveBtn.setAttribute('onclick', "sc_count('plan_list_01')")
footerReserveBtn.setAttribute('onclick', "sc_count('plan_list_02')")
}
}
import forEach from 'lodash/forEach'
import { isPointOn } from '../utils'
const hideTelContainer = container => {
const planCalBtnCourse = document.querySelector('.button--width-255 .js-popover-button')
if (!planCalBtnCourse) return
planCalBtnCourse.addEventListener('click', () => {
/* eslint no-param-reassign: 0 */
container.style.display = 'none'
})
}
const planCal = flg => {
const calendar = document.getElementById('popover_calendar')
// コース一覧では何もしない
if (!calendar) return
const calendarTitle = calendar.querySelector('.planCal__title__body')
const pointImg = calendar.querySelector('.issue84__reserve-point')
const telNumber = calendar.querySelector('.issue84__tel')
const closeBtnWrap = calendar.querySelector('.issue84__popover-close')
const closeBtns = calendar.querySelectorAll('.js-popover-close')
calendar.classList.add('issue84')
if (isPointOn) {
calendarTitle.parentNode.removeChild(calendarTitle)
} else {
calendarTitle.classList.add('-point-off')
pointImg.classList.add('-point-off')
}
pointImg.style.display = 'block'
closeBtnWrap.style.display = 'block'
// #3のみ電話番号枠表示
if (flg === 'tel') {
// 一旦表示
telNumber.style.display = 'block'
// ページ途中「おすすめコース」枠にある空席確認ボタンをタップした場合は電話番号枠を非表示
hideTelContainer(telNumber)
// モーダルを閉じた時は常にblock
forEach(closeBtns, closeBtn => {
closeBtn.addEventListener('touchstart', ev => {
ev.preventDefault()
// モーダルがフェードアウトする時間分ずらさせないと先に見えてしまう
const DELAY_TIME = 300
setTimeout(() => {
telNumber.style.display = 'block'
}, DELAY_TIME)
})
})
}
// 計測追加
// 空席確認モーダルのオリジナルコードで`.js-popover-close`にtouchstartが使われているのでclickでは動かない
forEach(closeBtns, closeBtn => {
closeBtn.addEventListener('touchstart', ev => {
ev.preventDefault()
window.sc_count('r-smp_plancal-modal_close')
})
})
}
export default planCal
import smoothscroll from 'smoothscroll-polyfill'
// Safariが behavior: 'smooth' オプションに対応してないので
// kick off the polyfill!
smoothscroll.polyfill()
const info = () => {
const infoWrap = document.querySelector('.topHeader')
// コース一覧では何もしない
if (!infoWrap) return
infoWrap.classList.add('issue84')
// Nodelistを配列に変換
const basicInfoAnchorWrapArray = Array.from(infoWrap.querySelectorAll('.topHeader__body'))
// メインが動画の場合にDOMが2つ存在するので最後(後の方)を指定
const basicInfoAnchorContainer = basicInfoAnchorWrapArray.slice(-1)[0]
const basicInfoAnchorWrap = document.createElement('p')
const basicInfoAnchor = document.createElement('a')
basicInfoAnchor.textContent = 'お店の詳細を見る'
basicInfoAnchor.setAttribute('href', '#basicInfo')
basicInfoAnchor.setAttribute('onclick', "sc_count('r-smp_goinfo')")
basicInfoAnchor.classList.add('text--minor', 'basic-info-anchor')
basicInfoAnchorWrap.appendChild(basicInfoAnchor)
basicInfoAnchorContainer.appendChild(basicInfoAnchorWrap)
basicInfoAnchor.addEventListener('click', ev => {
ev.preventDefault()
const targetId = ev.target.hash
const targetElm = document.querySelector(targetId)
const globalNavHeight = document.getElementById('js-globalNav').getBoundingClientRect().height
// 画面上部から要素までの距離
const rectTop = targetElm.getBoundingClientRect().top
// 現在のスクロール距離
const offsetTop = window.pageYOffset
// 追従するグロナビの高さを抜く
const top = rectTop + offsetTop - globalNavHeight
window.scrollTo({
top,
behavior: 'smooth',
})
})
}
export default info
以上是关于javascript Issue84 - 20190620电话导线改善的主要内容,如果未能解决你的问题,请参考以下文章
javascript Issue302:20171018 - 动画商品トライアル
javascript Issue319:20171101 - バナーとモーダルとAMP
javascript Issue15:20180705 - no79.CVエリア改善
javascript Issue30:20180920 - no82。他店铺リンク设置