PageSourceSearch

https://www.hondamobility-tohoku.co.jp/corporate/assets/js/common.js?012658

js hondamobility-tohoku.co.jp collected 2026-10-08 16:27:09 UTC 19,714 bytes, 669 lines download raw bytes

1'use strict';
2/*
3各ページ個別jsで拡張
4
5・on load
6MutJs.loaded = function() {}
7
8・scroll
9MutJs.scrolled = function() {}
10
11・resize
12MutJs.resized = function() {}
13
14・画像遅延ロード時
15MutJs.LazyImg.loaded = function() {}
16*/
17
18var MutJs = {
19  ticking: false,
20  loadingTimer: null,
21  isDesktopW: true,
22  winW: 0,
23  winH: 0,
24  scrTop: 0,
25  isLoaded: false,
26  init: function () {
27    this.sizeUpdate();
28    this.formatDOM();
29    this.LazyImg.init();
30    this.setHeaderFunc();
31    this.initTab();
32    this.setClickEvent();
33    this.setModal();
34    this.removeLoaderTimer();
35
36    if (!this.isLoaded) {
37      $(window).on('load', () => {
38        this.commonLoaded();
39      });
40    } else {
41      this.commonLoaded();
42    }
43
44    document.addEventListener('scroll', this.scrolling.bind(this), {
45      passive: true
46    });
47    window.addEventListener('resize', this.resizing.bind(this), {
48      passive: true
49    });
50  },
51  commonLoaded: function () {
52    this.isLoaded = true;
53    this.LazyImg.set();
54    this.setIntersection();
55    this.loaded();
56    this.removeLoader();
57    this.initXscrollbar();
58    this.setHashAnchor();
59    this.commonResized();
60    this.commonScrolled();
61  },
62  commonScrolled: function () {
63    this.scrTop = $(window).scrollTop();
64
65    if (this.scrTop > 200) {
66      $('body').addClass('is-sticky');
67    } else {
68      $('body').removeClass('is-sticky');
69    }
70
71    // ページトップ
72    this.scrolled();
73  },
74  commonResized: function () {
75    this.sizeUpdate();
76    this.resized();
77  },
78  scrolling: function () {
79    var that = this;
80    this.ticking = false;
81
82    if (!this.ticking) {
83      requestAnimationFrame(function () {
84        that.ticking = false;
85        that.commonScrolled();
86      });
87      this.ticking = true;
88    }
89  },
90  resizing: function () {
91    var that = this;
92    this.ticking = false;
93
94    if (!this.ticking) {
95      requestAnimationFrame(function () {
96        that.ticking = false;
97        that.commonResized();
98      });
99      this.ticking = true;
100    }
101  },
102  loaded: function () {},
103  scrolled: function () {},
104  resized: function () {},
105  sizeUpdate: function () {
106    this.winW = $(window).innerWidth();
107    this.winH = $(window).innerHeight();
108    this.isDesktopW = window.matchMedia('(min-width: 769px)').matches;
109    this.setViewport();
110
111    let vw = document.body.clientWidth * 0.01;
112    document.documentElement.style.setProperty('--vw', `${vw}px`);
113
114    let vh = window.innerHeight * 0.01;
115    document.documentElement.style.setProperty('--vh', `${vh}px`);
116  },
117  setViewport: function () {
118    // !mutUA.DesktopだけだとiOS13以上のiPadが拾えないので個別に判定
119    var ua = window.navigator.userAgent.toLowerCase();
120    var isPortable = !mutUA.Desktop || ua.indexOf('macintosh') && 'ontouchend' in document;
121
122    if (isPortable && this.winW <= 375) {
123      $('meta[name="viewport"]').attr('content', 'width=375');
124    }
125  },
126  removeLoaderTimer: function () {
127    this.loadingTimer = setTimeout(() => {
128      this.removeLoader();
129    }, 3000);
130  },
131  removeLoader: function () {
132    $('body').addClass('is-loaded');
133    clearTimeout(this.loadingTimer);
134  },
135  formatDOM: function () {
136    if (mutUA.Mobile && !mutUA.Desktop) {
137      $('.js-tel').each(function () {
138        $(this).addClass('_active');
139
140        if (!$(this).attr('href')) {
141          $(this).attr('href', 'tel:' + $(this).text().replace(/-/g, ''));
142        }
143      });
144    } else {
145      $('.js-tel').addClass('_inactive').removeAttr('href');
146    }
147  },
148  initXscrollbar: function() {
149    if (!this.isDesktopW) {
150      $('.js-x-scrollbar').each(function (i, el) {
151        if ($(this).find('li').length <= $(this).data('show-length')) {
152          $(this).addClass('no-bar');
153        }
154      });
155    }
156  },
157  initTab: function () {
158    // valで指定したdata-tabを持つタブに切り替える
159    function switchTab(val, groupFor = null) {
160      const $controller = $(`*[data-tab-for="${val}"]${groupFor ? `[data-tab-group-for="${groupFor}"]` : ``}`);
161      let $channel = $(`*[data-tab-id="${val}"]${groupFor ? `[data-tab-group-id="${groupFor}"]` : ''}`);
162
163      if (val === "all") {
164        // 'all'だったらグループごとに切り替えるか全体を対象にする
165        $channel = groupFor
166          ? $(`*[data-tab-id][data-tab-group-id="${groupFor}"]`)
167          : $('*[data-tab-id]');
168      }
169
170      if (groupFor) {
171        // グループが指定されている場合、同じグループ内のみ処理
172        $(`*[data-tab-for][data-tab-group-for="${groupFor}"]`).removeClass('is-current');
173        $controller.addClass('is-current');
174
175        $(`*[data-tab-id][data-tab-group-id="${groupFor}"]`).removeClass('is-current');
176        $channel.addClass('is-current');
177
178        if (groupFor === 'global') {
179          $("#js-global-menu-inner").animate({
180            scrollTop: $('#js-global-menu-head').innerHeight()
181          }, "200", "linear");
182        }
183      } else {
184        // グループが指定されていない場合、group指定がないもののみ処理
185        $('*[data-tab-for]').not('[data-tab-group-for]').removeClass('is-current');
186        $controller.addClass('is-current');
187
188        $('*[data-tab-id]').not('[data-tab-group-id]').removeClass('is-current');
189        $channel.addClass('is-current');
190      }
191
192      // select型の同期
193      const $select = $('select[data-tab-select]');
194      if ($select.length && $select.val() !== val) {
195        $select.val(val);
196      }
197
198      return false;
199    }
200
201    // click動作
202    $(document).on('click', '*[data-tab-for]', function (e) {
203      e.preventDefault();
204      const groupFor = $(this).data('tab-group-for') || null;
205      if (!$(this).hasClass('is-current')) {
206        switchTab($(this).data('tab-for'), groupFor);
207      }
208    });
209
210    // click動作
211    $(document).on('click', '*[data-tab-for]', function (e) {
212      e.preventDefault();
213
214      if (!$(this).hasClass('is-current')) {
215        switchTab($(this).data('tab-for'));
216      }
217    });
218
219    // select型の場合
220    $('body').on('change', 'select[data-tab-select]', function (e) {
221      switchTab($(this).val());
222    });
223
224    // #ハッシュ付きだったらそのタブをcurrentに
225    if (!!location.hash) {
226      var $hashTab = $('*[data-tab-id="' + location.hash.slice(1) + '"]');
227
228      if ($hashTab.length) {
229        switchTab(location.hash.slice(1));
230      }
231    }
232  },
233  setHeaderFunc: function () {
234    var that = this;
235    $('.js-header-nav-toggle').on('click', function (e) {
236      e.preventDefault();
237      $('body').toggleClass('is-toggle-menu-open');
238    });
239
240    // カテゴリーとタブの対応リスト
241    const categoryTabMap = {
242      index: "global-company",
243      company: "global-company",
244      democar: "global-newcar",
245      usedcar: "global-usedcar",
246      maintenance: "global-maintenance",
247      showroom: "global-showroom",
248      carlife: "global-carlife",
249      information: "global-company",
250      404: "global-company",
251    };
252
253    // body の data-category 属性値を取得
254    const category = $("body").data("global-nav");
255
256    // 対応するタブがある場合
257    if (categoryTabMap[category]) {
258      const tabValue = categoryTabMap[category];
259
260      // 該当の data-tab-for と data-tab-id に .is-current を付与
261      $(`[data-tab-for="${tabValue}"]`).addClass("is-current").css('order', '-1');
262      $(`[data-tab-id="${tabValue}"]`).addClass("is-current");
263    }
264  },
265  openModal: function (id) {
266    $('*[data-modal-id="' + id + '"]').addClass('is-show');
267  },
268  closeModal: function () {
269    $('*[data-modal-id]').removeClass('is-show');
270  },
271  setModal: function() {
272    var that = this;
273    
274    // click動作
275    $(document).on('click', '*[data-modal-for]', function (e) {
276      e.preventDefault();
277      that.openModal($(this).attr('data-modal-for'));
278    });
279    
280    $(document).on('click', '*[data-modal-contents]', function (e) {
281      e.stopPropagation();
282    });
283
284    $(document).on('click', '*[data-modal-close]', function (e) {
285      e.preventDefault();
286      that.closeModal();
287    });
288  },
289  setIntersection: function () {
290    // observeしたときのaction定義
291    const action = entry => {
292      // entry.target に検出したDOMが入ってくる
293      // entry.isIntersecting に画面内かどうかが入ってくる
294      const $target = $(entry.target);
295
296      if (!$target.attr('data-intersection')) {
297        $target.attr('data-intersection', 'once');
298      }
299
300      // onceの設定だったらtrueを消さない
301      if ($target.attr('data-intersection') != 'repeat' && $target.attr('data-intersecting') == 'true') {
302        return false;
303      }
304
305      $target.attr('data-intersecting', entry.isIntersecting);
306    };
307
308    // IntersectionObserverの利用
309    const observer = new IntersectionObserver(entries => {
310      entries.forEach(entry => {
311        action(entry);
312      });
313    }, {
314      rootMargin: '0px 0px -30px 0px'
315    });
316
317    // observeするDOMを取得
318    const $targets = $('*[data-intersection]');
319    $targets.each(function (j, el) {
320      observer.observe(el);
321    });
322  },
323  anchorScroll: function (hash) {
324    var speed = 500;
325    var hdrH = $('#js-header').height();
326    let pageAnchorH = !this.isDesktopW && $('.js-page-anchor').length ? $('.js-page-anchor').height() + 15 : 0;
327
328    if (hash == "#" || hash == "") {
329      var $target = $('html');
330    } else {
331      var $target = $('*[data-anchor-id="' + hash.slice(1) + '"]');
332    }
333
334    if ($target.length) {
335      var position = $target.offset().top;
336
337      var anchorPosition = position - hdrH - pageAnchorH;
338      $("html, body").animate({
339        scrollTop: anchorPosition
340      }, speed);
341    }
342  },
343  setHashAnchor: function () {
344    if (location.hash) {
345      this.anchorScroll(location.hash);
346    }
347  },
348  setClickEvent: function () {
349    var that = this;
350
351    // アンカースクロール
352    $(document).on('click', '.js-scroll', function (e) {
353      e.preventDefault();
354      var anchorTarget = '#' + $(this).attr("href").split('#')[1];
355      that.anchorScroll(anchorTarget);
356    });
357
358    // 汎用toggle
359    $(document).on('click', '*[data-toggle-for], *[data-toggle-open]', function (e) {
360      e.preventDefault();
361      
362      var toggleId = $(this).data('toggle-for') || $(this).data('toggle-open');
363      var isOpenOnly = $(this).is('[data-toggle-open]');
364
365      if (!toggleId) return;
366
367      var toggleTarget = $('*[data-toggle-id="' + toggleId + '"]');
368
369      if (toggleTarget.length) {
370        if (isOpenOnly) {
371          toggleTarget.addClass('is-show').slideDown();
372        } else {
373          $(this).toggleClass('is-show');
374          toggleTarget.toggleClass('is-show');
375          if (!$(this).data('toggle-show') || $(this).data('toggle-show') !== 1) {
376            toggleTarget.slideToggle();
377          }
378        }
379      }
380    });
381
382    $('.js-sitemap-toggle').each(function(){
383
384      if ($(this).closest('[data-category]').attr('data-category') == "newcar") {
385        // 新車のときはは例外的にページ1つでもtoggleを展開
386        return true;
387      }
388
389      if ($(this).parent().next().find('li').length < 2) {
390        let href = $(this).parent().next().find('a').attr('href');
391        let target = $(this).parent().next().find('a').attr('target') || '_self';
392        let modalFor = $(this).parent().next().find('a').attr('data-modal-for');
393        $(this).attr('href', href).attr('target', target);
394        if (modalFor) {
395          $(this).attr('data-modal-for', modalFor);
396        }
397      }
398    });
399
400    // sitemap toggle
401    $(document).on('click', '.js-sitemap-toggle', function (e) {
402      if (!$(this).attr('href')) {
403        e.preventDefault();
404        $(this).toggleClass('is-open').parent().next().slideToggle();
405      }
406    });
407
408    // common toggle
409    $(document).on('click', '.js-next-toggle', function (e) {
410      e.preventDefault();
411      $(this).toggleClass('is-open').next().slideToggle(200);
412    });
413
414    $(document).on('click', '.js-faq-toggle', function (e) {
415      e.preventDefault();
416      $(this).toggleClass('is-open').parent().next().slideToggle(200);
417    });
418  },
419  LazyImg: {
420    image: $('img[data-src]'),
421    imageLen: $('img[data-src]').length,
422    bg: $('*[data-bg-image]'),
423    loadedLen: 0,
424    init: function () {
425      this.image.attr('src', ''); //console.log(this.imageLen);
426
427      if (!this.imageLen) {
428        this.desvg();
429        this.commonLoaded();
430        this.loaded();
431      } else {
432        this.image.bind('load', () => {
433          this.loadedLen++; //console.log(this.loadedLen);
434
435          if (this.loadedLen == this.imageLen) {
436            this.desvg();
437            this.commonLoaded();
438            this.loaded();
439          }
440        });
441      }
442    },
443    set: function () {
444      this.image.each(function (i) {
445        $(this).attr('src', $(this).attr('data-src'));
446      });
447    },
448    desvg: function () {
449      desvg('[src$=".svg"]', true);
450    },
451    commonLoaded: function () {
452      this.bg.each(function (i) {
453        $(this).attr('style', 'background-image: url(' + $(this).attr('data-bg-image') + ')');
454      });
455    },
456    loaded: function () {
457      return false;
458    }
459  }
460};
461$(window).on('load', () => {
462  MutJs.isLoaded = true;
463});
464$(function () {
465  MutJs.init();
466});
467
468// svgファイルの色変え用に展開する
469function desvg(selector, removeInlineCss) {
470  removeInlineCss = removeInlineCss || false;
471
472  var images,
473      imagesLength,
474      sortImages = {},
475  // load svg file
476  loadSvg = function (imgURL, replaceImages) {
477    // set up the AJAX request
478    var xhr = new XMLHttpRequest();
479    xhr.open('GET', imgURL, true);
480
481    xhr.onload = function () {
482      var xml, svg, paths, replaceImagesLength;
483
484      // get the response in XML format
485      xml = xhr.responseXML;
486      replaceImagesLength = replaceImages.length;
487
488      // bail if no XML
489      if (!xml) {
490        return;
491      }
492
493      // this will be the <svg />
494      svg = xml.documentElement;
495
496      // get all the SVG paths
497      paths = svg.querySelectorAll('path');
498
499      if (removeInlineCss) {
500        // if `removeInlineCss` is true then remove the style attributes from the SVG paths
501        for (var i = 0; i < paths.length; i++) {
502          paths[i].removeAttribute('style');
503        }
504      }
505
506      svg.removeAttribute('xmlns:a');
507
508      while (replaceImagesLength--) {
509        replaceImgWithSvg(replaceImages[replaceImagesLength], svg.cloneNode(true));
510      }
511    };
512
513    xhr.send();
514  },
515  // replace the original <img /> with the new <svg />
516  replaceImgWithSvg = function (img, svg) {
517    var imgID = img.id,
518        imgClasses = img.getAttribute('class');
519
520    if (imgID) {
521      // re-assign the ID attribute from the <img />
522      svg.id = imgID;
523    }
524
525    if (imgClasses) {
526      // re-assign the class attribute from the <img />
527      svg.setAttribute('class', imgClasses + ' replaced-svg');
528    }
529
530    svg.setAttribute('data-src', img.getAttribute('src'));
531
532    if (img.parentNode) {
533      img.parentNode.replaceChild(svg, img);
534    }
535  };
536
537  // grab all the elements from the document matching the passed in selector
538  images = document.querySelectorAll(selector);
539  imagesLength = images.length;
540
541  // sort images array by image url
542  while (imagesLength--) {
543    var _img = images[imagesLength],
544        _imgURL;
545
546    if (_img.getAttribute('data-src')) {
547      _imgURL = _img.getAttribute('data-src');
548    } else {
549      _imgURL = _img.getAttribute('src');
550    }
551
552    if (sortImages[_imgURL]) {
553      sortImages[_imgURL].push(_img);
554    } else {
555      sortImages[_imgURL] = [_img];
556    }
557  }
558
559  // loops over the matched urls
560  for (var key in sortImages) {
561    if (sortImages.hasOwnProperty(key)) {
562      loadSvg(key, sortImages[key]);
563    }
564  }
565}
566
567// slickAddClassMoving
568var slickAddClassMoving = function (slider) {
569  slider.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
570    /*
571    [遷移中のcloneのcurrent表示の調整]
572    */
573
574    let $this = $(this);
575    let isLastSlideNext = currentSlide - nextSlide === slick.slideCount - 1;
576    let isFirstSlidePrev = nextSlide - currentSlide === slick.slideCount - 1;
577    let $target = $this.find('.slick-slide[data-slick-index="' + nextSlide + '"]');
578
579    if (isLastSlideNext) {
580      // 最後のスライド→最初のスライド への遷移の場合
581      $target = $this.find('.slick-slide[data-slick-index="' + (currentSlide + 1) + '"]');
582      $this.find('.slick-slide').addClass('last-to-first');
583    }
584
585    if (isFirstSlidePrev) {
586      // 最初のスライド→最後のスライド への遷移の場合
587      $target = $this.find('.slick-slide[data-slick-index="' + (currentSlide - 1) + '"]');
588      $this.find('.slick-slide').addClass('first-to-last');
589    }
590
591    $this.find('.slick-slide').removeClass('is-moving');
592    $target.addClass('is-moving');
593  });
594
595  slider.on('afterChange', function (event, slick, currentSlide) {
596    $(this).find('.slick-slide').removeClass('last-to-first').removeClass('first-to-last').removeClass('is-moving');
597  });
598};
599
600/*
601アンカーリンクのカレント表示用
602*/
603if ($('.js-page-anchor').length) {
604  let previousAnchorId = null;
605
606  $(window).on('scroll', function () {
607    const scrollTop = $(window).scrollTop();
608    const viewportMiddle = scrollTop + ($(window).height() / 2);
609
610    let currentAnchorId = null;
611
612    if ($('.js-page-anchor').offset().top - $('#js-header').innerHeight() <= scrollTop) {
613      $('.js-page-anchor').addClass('is-sticky');
614    } else {
615      $('.js-page-anchor').removeClass('is-sticky');
616    }
617
618    // カレント表示を更新
619    $('[data-anchor-id]').each(function () {
620      const $this = $(this);
621      const offsetTop = $this.offset().top;
622      const offsetBottom = offsetTop + $this.outerHeight();
623
624      // 条件: 上端がビューポートの半分より上に達したときカレント表示
625      // 条件: 下端がビューポートの半分より上に達したときカレント解除
626      if (offsetTop <= viewportMiddle && offsetBottom > viewportMiddle) {
627        currentAnchorId = $this.data('anchor-id');
628        return false; // 最初に該当する要素を見つけたらループを終了
629      }
630    });
631
632    // 前回と現在のカレントIDが同じなら何もしない
633    if (currentAnchorId === previousAnchorId) {
634      return;
635    }
636
637    // 全てのカレント表示をリセット
638    $('.js-scroll.is-current, [data-anchor-id].is-current').removeClass('is-current');
639
640    if (currentAnchorId) {
641      $(`[data-anchor-id="${currentAnchorId}"]`).addClass('is-current');
642      $(`.js-scroll[href="#${currentAnchorId}"]`).addClass('is-current');
643
644      // 横スクロールでカレント要素を表示
645      const $currentLink = $(`.js-scroll[href="#${currentAnchorId}"]`);
646      const $wrapper = $currentLink.closest('.js-page-anchor-scroll');
647
648      if ($wrapper.length) {
649        const wrapperScrollLeft = $wrapper.scrollLeft();
650        const wrapperWidth = $wrapper.width();
651        const linkOffsetLeft = $currentLink.offset().left - $wrapper.offset().left;
652        const linkWidth = $currentLink.outerWidth();
653
654        if (linkOffsetLeft < 0) {
655          // 左端が見切れ
656          const scrollTo = wrapperScrollLeft + linkOffsetLeft;
657          $wrapper.animate({ scrollLeft: scrollTo }, 300);
658        } else if (linkOffsetLeft > 0 && linkOffsetLeft + linkWidth > wrapperScrollLeft + wrapperWidth) {
659          // 右端が見切れ
660          const scrollTo = linkOffsetLeft + linkWidth - wrapperScrollLeft - wrapperWidth;
661          $wrapper.animate({ scrollLeft: scrollTo }, 300);
662        }
663      }
664    }
665
666    // カレントIDを更新
667    previousAnchorId = currentAnchorId;
668  });
669}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.