PageSourceSearch

https://tanp.jp/carts

html tanp.jp collected 2026-10-02 03:51:45 UTC 38,849 bytes, 1,141 lines download raw bytes

1
2<!DOCTYPE html>
3<html lang="ja">
4<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
5    <meta charset="utf-8"/>    <meta name="viewport" content="width=device-width,initial-scale=1.0">
6    <meta name="google-site-verification" content="AD6C_I-u_IpdT7-l1nRqU1mo-JcGVY2Q_JDPLrOsjI4">
7    <link rel="shortcut icon" href="https://assets.tanp.jp/favicon.ico">
8    <title>ショッピングカート | TANP(タンプ)</title>
9    
9<script src="https://assets.tanp.jp/72a9040/js/laravel-mix/dist/pc/init.js"></script>
9    <meta name="description" content="日本最大級のギフト専門通販TANP(タンプ)があなたのギフト選びを総合的にお手伝いいたします。【25,000点以上の品揃え】【ポイント還元最大5%】【最短即日発送】 専門バイヤーが厳選したセンスのよいお洒落なギフトに出会えます。充実のサポートで適切なギフトが簡単に選べるのでネットでの購入も安心です。"/>            <link href="https://tanp.jp/carts" rel="canonical">
10                    <meta name="robots" content="noindex">
11            <meta property="og:title" content="ショッピングカート | TANP(タンプ)">
12    <meta property="og:type" content="article">
13            <meta property="og:url" content="https://tanp.jp/carts">
14        <meta property="og:image"
15          content="https://images.tanp.jp/gracia.tanp/ogp/tanp_ogp.jpg">
16    <meta property="og:site_name" content="TANP(タンプ)">
17            <meta property="og:description" content="日本最大級のギフト専門通販TANP(タンプ)があなたのギフト選びを総合的にお手伝いいたします。【25,000点以上の品揃え】【ポイント還元最大5%】【最短即日発送】 専
17門バイヤーが厳選したセンスのよいお洒落なギフトに出会えます。充実のサポートで適切なギフトが簡単に選べるのでネットでの購入も安心です。">
18        <link rel="apple-touch-icon" href='https://assets.tanp.jp/favicon.ico'/>
19    <meta property="fb:app_id" content="130413917573250">
20    <meta name="twitter:card" content="summary_large_image">
21
22    <meta name="p:domain_verify" content="b66815b7fca76d124c9854d862c15848"/>
23
24    
25
26
27<script>
28    window.dataLayer = window.dataLayer || [];
vendor: 366 bytes, lines 28-31
28
29                        
30    
31            (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','
31GTM-54ST37K
vendor: 13 bytes, lines 31-33
31');
32    
33    
33</script>
33
34
35            <link rel="stylesheet" href="https://assets.tanp.jp/72a9040/css/dist/styles/pc-application.css"/>        <link rel="stylesheet" href="https://assets.tanp.jp/72a9040/css/dist/styles/pages/common/carts/index.css"/><link rel="stylesheet" href="https://assets.tanp.jp/72a9040/css/dist/pages/favorite_toast/index.css"/>
36        <link rel="preload" href="https://assets.tanp.jp/font/NotoSansJP-Regular-Slim.woff2" as="font" type="font/woff2" crossorigin>
37    <link rel="preload" href="https://assets.tanp.jp/font/NotoSansJP-Bold-Slim.woff2" as="font" type="font/woff2" crossorigin>
38    <link rel="preload" href="https://assets.tanp.jp/font/FontAwesome-Slim.woff2" as="font" type="font/woff2" crossorigin>
39
40    
41                    
41<script src="https://assets.tanp.jp/72a9040/js/dist/my_layout.js"></script>
41    
42    
42<script src="https://assets.tanp.jp/72a9040/js/laravel-mix/dist/pc/application.js" defer="defer"></script>
42    </head>
43
44<body>
45
46
47<template id='template-product-item'>
48    <a href="" class="container">
49        <div class="image-container">
50            <p class="rank"></p>
51            <div class="label-container">
52                <p class="can-personalization">名入れ対応</p>
53                <p class="is-new">New</p>
54            </div>
55            <img class="image">
56        </div>
57        <div class="info-container">
58            <p class="brand-title text"></p>
59            <p class="title text"></p>
60            <p class="product-group text"></p>
61            <div class="bottom-container">
62                <div class="price-container">
63                    <p class="price text"></p>
64                </div>
65                <slot name="fav-button"></slot>
66            </div>
67            <p class="min-shipping-date"></p>
68            <p class="sold-out">SOLD OUT</p>
69        </div>
70    </a>
71
72    <style>
73        * {
74            box-sizing: border-box;
75        }
76
77        p {
78            margin: 0;
79        }
80
81        :host {
82            /* デフォルトはinline-block。そのままだとcontainerのwidth:100%が効かない */
83            display: block;
84        }
85
86        .container {
87            display: block;
88            width: 100%;
89            transition: opacity 0.3s;
90            text-decoration: none;
91        }
92
93        .image-container {
94            position: relative;
95            width: 100%;
96            padding-top: 100%;
97        }
98
99        .rank {
100            position: absolute;
101            z-index: 1;
102            margin: 0;
103            left: 5px;
104            top: 5px;
105            font-size: 11px;
106            display: inline-block;
107            height: 22px;
108            line-height: 22px;
109            border-radius: 12px;
110            padding: 0 9px;
111            color: var(--color-base-bright);
112            background-color: var(--color-primary);
113        }
114
115        :host(:not([rank])) .rank {
116            display: none;
117        }
118
119        .label-container {
120            position: absolute;
121            z-index: 1;
122            left: 0;
123            bottom: 0;
124            display: flex;
125            flex-direction: column;
126            align-items: flex-start;
127        }
128
129        .can-personalization,
130        .is-new {
131            font-size: 10px;
132            line-height: 12px;
133            color: var(--color-base-bright);
134            padding: 4px 8px;
135        }
136
137        .can-personalization {
138            background-color: #EF5D60;
139        }
140
141        :host(:not([can-personalization])) .can-personalization {
142            display: none;
143        }
144
145        .is-new {
146            background-color: #C9B67E;
147        }
148
149        :host(:not([is-new])) .is-new {
150            display: none;
151        }
152
153        .image {
154            display: block;
155            position: absolute;
156            top: 0;
157            left: 0;
158            width: 100%;
159            height: 100%;
160            object-fit: cover;
161            border: 0;
162            opacity: 0;
163            transition: opacity 0.5s;
164        }
165
166        .image[src] {
167            opacity: 1 !important;
168        }
169
170        .info-container {
171            padding-top: 8px;
172        }
173
174        .text {
175            line-height: 1.4;
176            white-space: nowrap;
177            overflow: hidden;
178            text-overflow: ellipsis;
179        }
180
181        .brand-title {
182            font-size: 10px;
183            color: var(--color-base-deep);
184            margin-bottom: 4px;
185        }
186
187        .title {
188            font-size: 12px;
189            font-weight: bold;
190            margin-bottom: 4px;
191            color: var(--color-base);
192        }
193
194        .product-group {
195            font-size: 12px;
196            color: var(--color-base-deep);
197            margin-bottom: 8px;
198        }
199
200        .bottom-container {
201            display: flex;
202            justify-content: space-between;
203            align-items: flex-start;
204            margin-bottom: 8px;
205        }
206
207        .price {
208            font-size: 14px;
209            line-height: 20px;
210            color: var(--color-base);
211        }
212
213        .sold-out {
214            display: none;
215            font-size: 10px;
216            line-height: 10px;
217            color: var(--color-base-deep);
218            background-color: var(--color-base-middle);
219            border-radius: 4px;
220            padding: 6px 8px;
221        }
222
223        :host([sold-out]) .sold-out {
224            display: inline-block;
225        }
226
227        .min-shipping-date {
228            display: none;
229            font-size: 12px;
230            font-weight: normal;
231            color: var(--color-base);
232        }
233
234        .min-shipping-date span {
235            color: var(--color-secondary);
236            font-weight: bold;
237        }
238
239        :host([min-shipping-date]) .min-shipping-date {
240            display: block;
241        }
242
243        :host([device="sp"]) .rank {
244            left: 4px;
245            top: 4px;
246            font-size: 10px;
247        }
248
249        :host([device="sp"]) .info-container {
250            padding: 8px;
251        }
252
253        :host([device="sp"]) .title {
254            font-size: 10px;
255            min-height: 28px;
256            white-space: normal;
257            display: -webkit-box;
258            -webkit-box-orient: vertical;
259            -webkit-line-clamp: 2;
260        }
261
262        :host([device="sp"]) .product-group {
263            font-size: 10px;
264        }
265
266        :host([device="sp"]) .price {
267            font-size: 16px;
268            line-height: 1.2;
269        }
270
271        :host([device="sp"]) .min-shipping-date {
272            font-size: 10px;
273        }
274    </style>
275</template>
276
277<script>
278    customElements.define('product-item',
279        class extends HTMLElement {
280            constructor() {
281                super();
282
283                // このElementにShadow DOMをattachする
284                const shadowRoot = this.attachShadow({mode: 'open'});
285
286                // <template> タグの内容を複製し、Shadow DOMに追加
287                const template = document.getElementById('template-product-item');
288                shadowRoot.appendChild(template.content.cloneNode(true));
289            }
290
291            connectedCallback() {
292                // linkの設定
293                const linkElement = this.shadowRoot.querySelector('.container');
294                linkElement.href = `/products/view/${this.productId}`;
295
296                // rankの設定
297                const rankElement = this.shadowRoot.querySelector('.rank');
298                if (this.rank) {
299                    rankElement.innerText = `${this.rank} 位`;
300                }
301
302                // imageの設定
303                // TODO: loading: lazy属性が普及したらそれに置き換える
304                const imageElement = this.shadowRoot.querySelector('.image');
305                imageElement.alt = this.title;
306                const this_ = this;
307                // 画面に入ったときに画像を読み込む
308                this.observer = new IntersectionObserver((entries, observer) => {
309                    const entry = entries[0];
310                    if (entry.isIntersecting) {
311                        observer.unobserve(entry.target);
312                        entry.target.src = this_.imageUrl;
313                    }
314                }, {rootMargin: '1000px 0px'});
315                this.observer.observe(imageElement);
316
317                // brand titleの設定
318                const brandTitleElem = this.shadowRoot.querySelector('.brand-title');
319                brandTitleElem.innerText = this.brandTitle;
320
321                // titleの設定
322                const titleElem = this.shadowRoot.querySelector('.title');
323                titleElem.innerText = this.title;
324
325                // product groupの設定
326                const productGroupElem = this.shadowRoot.querySelector('.product-group');
327                productGroupElem.innerText = this.productGroup;
328
329                // priceの設定
330                const priceElem = this.shadowRoot.querySelector('.price');
331                if (this.shouldDisplayPriceRangeMark) {
332                    priceElem.innerHTML = `&yen; ${this.price.toLocaleString()}〜`;
333                } else {
334                    priceElem.innerHTML = `&yen; ${this.price.toLocaleString()}`;
335                }
336
337                // min-shipping-dateの設定
338                if (this.minShippingDate) {
339                    const minShippingDateElem = this.shadowRoot.querySelector('.min-shipping-date');
340                    minShippingDateElem.innerHTML = `最短<span>${this.minShippingDate.toLocaleString()}</span>お届け`;
341                }
342            }
343
344            /*
345             * Attributes
346             */
347            get productId() {
348                return this.getAttribute('product-id');
349            }
350
351            get rank() {
352                return this.getAttribute('rank');
353            }
354
355            get imageUrl() {
356                return this.getAttribute('image-url');
357            }
358
359            get title() {
360                return this.getAttribute('title');
361            }
362
363            get brandTitle() {
364                return this.getAttribute('brand-title');
365            }
366
367            get productGroup() {
368                return this.getAttribute('product-group');
369            }
370
371            get price() {
372                return Number(this.getAttribute('price'));
373            }
374
375            get shouldDisplayPriceRangeMark() {
376                const val = this.getAttribute('should-display-price-range-mark');
377                return val === '' || val === 'true';
378            }
379
380            get minShippingDate() {
381                return this.getAttribute('min-shipping-date');
382            }
383
384            get device() {
385                return this.getAttribute('device') ?? 'pc';
386            }
387        }
388    );
389</script>
389
390
391<template id='template-login-modal'>
392    <div class="background">
393        <div class="modal">
394            <div class="header">
395                <span class="close-button">&times;</span>
396            </div>
397            <div class="content">
398                <div class="content-image-box">
399                    <img class="content-image"
400                         src="https://images.tanp.jp/gracia.tanp/icons/login_modal_img.png"
401                         alt="login_modal_img"
402                    >
403                </div>
404                <p class="content-title">お気に入りに追加</p>
405                <p class="content-sub-title">「再入荷」や「値下げ」をすぐにお知らせ</p>
406                <p class="content-description">
407                    メルマガやアプリプッシュ通知を<br>
408                    許可していただいた方にのみ送付いたします。
409                </p>
410                <p class="content-attention">お気に入りの追加にはログインが必要です</p>
411                <div class="login-link-container">
412                    <a href="/users/login" class="login-link">
413                        <button class="login-link-container-button login">ログイン</button>
414                    </a>
415                    <a href="/users/registrationForm" class="login-link">
416                        <button class="login-link-container-button signUp">新規会員登録</button>
417                    </a>
418                </div>
419            </div>
420        </div>
421    </div>
422
423    <style>
424        * {
425            box-sizing: border-box;
426        }
427
428        .background {
429            z-index: 100;
430            position: fixed;
431            left: 0;
432            top: 0;
433            width: 100vw;
434            height: 100vh;
435            background-color: rgba(0, 0, 0, 0.4);
436            animation: fade-in 0.8s;
437        }
438
439        @keyframes fade-in {
440            from {
441                opacity: 0;
442            }
443            to {
444                opacity: 1;
445            }
446        }
447
448        .modal {
449            max-width: 406px;
450            width: calc(100% - 20px);
451            background-color: var(--color-base-bright);
452            border-radius: 5px;
453            margin: calc((100vh - 596.5px) / 2) auto;
454        }
455
456        :host([device="sp"]) .modal {
457            max-width: 343px;
458            margin: calc((100vh - 530px) / 2) auto;
459        }
460
461        .header {
462            position: relative;
463            padding: 20px;
464        }
465
466        :host([device="sp"]) .header {
467            padding: 16px:
468        }
469
470        .close-button {
471            position: absolute;
472            right: 21px;
473            top: 21px;
474            font-size: 24px;
475            color: var(--color-base-deep);
476            cursor: pointer;
477        }
478
479        .content {
480            padding: 20px;
481        }
482
483        :host([device="sp"]) .content {
484            padding: 16px;
485        }
486
487        .content-image-box {
488            position: relative;
489            margin: 0 auto;
490            height: 162px;
491            width: 162px;
492        }
493
494        :host([device="sp"]) .content-image-box {
495            height: 137px;
496            width: 137px;
497        }
498
499        .content-image-box img {
500            object-fit: contain;
501            position: absolute;
502            width: 100%;
503            height: 100%;
504        }
505
506        .content-title {
507            font-size: 24px;
508            text-align: center;
509            font-weight: 700;
510            line-height: 150%;
511        }
512
513        :host([device="sp"]) .content-title {
514            font-size: 18px;
515        }
516
517        .content-sub-title {
518            font-size: 18px;
519            text-align: center;
520            font-weight: 700;
521            line-height: 100%;
522        }
523
524        :host([device="sp"]) .content-sub-title {
525            font-size: 14px;
526        }
527
528        .content-description {
529            font-size: 12px;
530            text-align: center;
531            font-weight: 400;
532            color: var(--color-base-deep);
533        }
534
535        :host([device="sp"]) .content-description {
536            font-size: 10px;
537        }
538
539        .content-attention {
540            font-size: 14px;
541            text-align: center;
542            font-weight: 400;
543            color: var(--color-notification);
544            margin-top: 32px;
545        }
546
547        :host([device="sp"]) .content-attention {
548            font-size: 12px;
549        }
550
551        .login-link-container {
552            margin-top: 16px;
553            text-align: center;
554            padding: 0 11px;
555        }
556
557        :host([device="sp"]) .login-link-container {
558            font-size: 15px;
559        }
560
561        .login-link-container-button {
562            width: 100%;
563            height: 48px;
564            margin-bottom: 16px;
565            border-radius: 8px;
566            border: 1px solid #E4A357;
567            font-size: 15px;
568            line-height: 100%;
569            font-weight: 700;
570        }
571
572        :hover .login-link-container-button {
573            cursor: pointer;
574        }
575
576        .login {
577            background: var(--color-primary);
578            color: var(--color-base-bright);
579        }
580
581        .signUp {
582            background: var(--color-base-bright);
583            color: var(--color-primary);
584        }
585
586        .login-link {
587            font-size: 14px;
588            color: var(--color-notification);
589            text-decoration: underline;
590        }
591
592        :host([device="sp"]) .login-link {
593            font-size: 16px;
594            color: var(--color-primary);
595        }
596
597        .login-link:first-of-type {
598            margin-right: 30px;
599        }
600    </style>
601</template>
602
603<script>
604customElements.define('login-modal',
605  class extends HTMLElement {
606    constructor() {
607      super();
608
609      // このElement(<login-modal>タグ)にShadow DOMをattachする
610      const shadowRoot = this.attachShadow({mode: 'open'});
611
612      const template = document.getElementById('template-login-modal');
613      shadowRoot.appendChild(template.content.cloneNode(true));
614    }
615
616    connectedCallback() {
617      this.shadowRoot.querySelectorAll('.login-link').forEach((link) => {
618          link.href += '?redirect=' + this.getCurrentPath();
619      });
620
621      // close-buttonの設定
622      const closeButton = this.shadowRoot.querySelector('.close-button');
623      const this_ = this;
624      closeButton.addEventListener('click', () => {
625        this_.remove();
626      });
627    }
628
629    getCurrentPath() {
630      return window.location.pathname;
631    }
632  }
633);
634</script>
634
635<template id='template-login-modal-for-fav'>
636    <login-modal>
637    </login-modal>
638</template>
639
640<template id='template-fav-button'>
641    <button>
642        <svg>
643            <use xlink:href="/img/icons.svg#icon-heart-fill"></use>
644        </svg>
645    </button>
646
647    <style>
648        button {
649            box-sizing: border-box;
650            display: block;
651            padding: 0;
652            height: 20px;
653            width: 20px;
654            background: transparent;
655            border: none;
656        }
657
658        svg {
659            width: 100%;
660            height: 100%;
661            fill: var(--color-base-middle);
662        }
663
664        :host([favorited=""]) svg, :host([favorited="true"]) svg {
665            fill: var(--color-notification);
666        }
667    </style>
668</template>
669<script>
670    const FAVORITE_TOAST_EVENT = 'tanp:favorites:show-toast';
671
672    class FavButtonElement extends HTMLElement {
673
674        // 同一商品のお気に入り状態を同期するために、ページ内の全てのFavButtonElementを保持しておく(これを継承しているFavButtonWithCountElementも保持する)
675        static favoriteButtonElements = [];
676
677        constructor() {
678            super();
679            // shadow-domに隠された場合、window.querySelectorなどでは
680            // エレメントにアクセスできないため、エレメントのリストを別で管理する。
681            FavButtonElement.favoriteButtonElements.push(this);
682        }
683
684        // カスタム要素がDOMに接続された時に呼び出される
685        connectedCallback() {
686            if (!this.shadowRoot) {
687                // このElement(<fav-button>タグ)にShadow DOMをattachする
688                const shadowRoot = this.attachShadow({mode: 'open'});
689
690                // <template> タグの内容を複製し、Shadow DOMに追加
691                const template = document.getElementById('template-fav-button');
692                shadowRoot.appendChild(template.content.cloneNode(true));
693
694                // ボタンをクリックした時の挙動をセットする
695                this.setButtonEvent();
696            }
697        }
698
699        setButtonEvent() {
700            this.isRequesting = false;
701
702            const button = this.shadowRoot.querySelector('button');
703
704            // アイコンをクリックした時の挙動
705            button.addEventListener('click', (e) => {
706                e.stopPropagation();
707                e.preventDefault();
708
709                // "show-modal-on-click" 属性が指定されている場合は
710                // モーダルを表示して処理を終える
711                if (this.showModalOnClick) {
712                    const modalTemplate = document.getElementById('template-login-modal-for-fav');
713                    const modalNode = modalTemplate.content.cloneNode(true);
714                    // "device" attributeを設定する
715                    modalNode.querySelector('login-modal').setAttribute('device', this.device);
716                    document.body.appendChild(modalNode);
717                    return;
718                }
719
720                if (this.shouldOpenFavoriteListSelector) {
721                    window.dispatchEvent(new CustomEvent('tanp:favorites:open-selector', {
722                        detail: {
723                            productId: Number(this.productId),
724                            logAddFavEvent: this.logAddFavEvent,
725                            logDeleteFavEvent: this.logDeleteFavEvent,
726                        },
727                    }));
728                    return;
729                }
730
731                if (this.isRequesting === false) {
732                    // 2重リクエストが起きないようにする
733                    this.isRequesting = true;
734                    const nextFavorited = !this.isFavorited;
735
736                    // requestを送信する
737                    this.requestFavorite(nextFavorited)
738                        .then((res) => {
739                            if (res.ok) {
740                                // 表示を切り替える
741                                this.brotherElements.forEach((elem) => elem.toggle());
742                                // ログを送信する(エラー処理はしない)
743                                this.pushLog(this.isFavorited);
744                                this.showFavoriteToast(nextFavorited);
745                            }
746                        })
747                        .finally(() => {
748                            // 処理が終わったのでisRequestingを更新する
749                            this.isRequesting = false;
750                        });
751                }
752            });
753        }
754
755        // 'favorited' attributeを切り替える
756        toggle() {
757            this.isFavorited = !this.isFavorited;
758        }
759
760        // favorite状態の更新リクエストをサーバーに送る
761        // - val : Boolean
762        // - requestFavorite() : Promise<Response>
763        requestFavorite(val) {
764            const path = val ? '/favorites/add' : '/favorites/delete';
765            const csrfToken = document.querySelector('input[name="_csrfToken"]').value;
766            const formData = new FormData();
767            formData.append('product_id', this.productId);
768            formData.append('is_published', this.isPublishedList);
769
770            return fetch(path, {
771                method: 'POST',
772                credentials: 'same-origin',
773                headers: {
774                    'X-CSRF-Token': csrfToken,
775                },
776                body: formData,
777            });
778        }
779
780        pushLog(val) {
781          // TanpLoggerはlogger.jsファイルで定義されている
782          if (window.TanpLogger === undefined) {
783            return;
784          }
785
786          const type = val ? this.logAddFavEvent : this.logDeleteFavEvent;
787
788          const logObj = {
789            type,
790            product_id: this.productId,
791          };
792          window.TanpLogger.pushStandardLog(logObj);
793        }
794
795        showFavoriteToast(isFavorited) {
796            window.dispatchEvent(new CustomEvent(FAVORITE_TOAST_EVENT, {
797                detail: {
798                    isPublished: this.isPublishedList,
799                    favorited: isFavorited,
800                },
801            }));
802        }
803
804        /*
805         * Attributes
806         */
807        get productId() {
808            return this.getAttribute('product-id');
809        }
810
811        get isFavorited() {
812            const val = this.getAttribute('favorited');
813            return val === '' || val === 'true';
814        }
815
816        set isFavorited(val) {
817            if (val) {
818                this.setAttribute('favorited', '');
819            } else {
820                this.removeAttribute('favorited');
821            }
822        }
823
824        get showModalOnClick() {
825            const val = this.getAttribute('show-modal-on-click');
826            return val === '' || val === 'true';
827        }
828
829        get shouldOpenFavoriteListSelector() {
830            return false;
831        }
832
833        get device() {
834            const val = this.getAttribute('device');
835            return val ?? 'pc';
836        }
837
838        get logAddFavEvent() {
839            return this.getAttribute('log-add-fav-event') ?? 'add_favorite';
840        }
841
842        get logDeleteFavEvent() {
843            return this.getAttribute('log-delete-fav-event') ?? 'delete_favorite';
844        }
845
846        get isPublishedList() {
847            const val = this.getAttribute('is-published');
848            if (val === null) {
849                return false;
850            }
851
852            return val === '' || val === 'true' || val === '1';
853        }
854
855        /*
856         * Computed fields
857         */
858
859        // 自分と同じ商品IDをもつお気に入りボタンを返す
860        get brotherElements() {
861            return FavButtonElement.favoriteButtonElements.filter((element) => {
862                return element.productId === this.productId
863                    && element.isPublishedList === this.isPublishedList;
864            });
865        }
866    }
867
868    // <fav-button> タグの登録
869    customElements.define('fav-button', FavButtonElement);
870</script>
870
871
872<template id='template-fav-button-with-count'>
873    <button>
874        <svg class="icon">
875            <use xlink:href="/img/icons.svg#icon-heart-fill"></use>
876        </svg>
877        <span class="count"></span>
878    </button>
879    <style>
880        * {
881            box-sizing: border-box;
882            padding: 0;
883        }
884
885        button {
886            display: block;
887            padding: 2px;
888            background: transparent;
889            border: none;
890            margin: 0 auto;
891            width: 100%;
892            height: 100%;
893            cursor: pointer;
894        }
895
896        .icon {
897            display: block;
898            margin: 0 auto;
899            width: 24px;
900            height: 24px;
901            fill: var(--color-base-middle);
902        }
903
904        .count {
905            display: block;
906            font-size: 10px;
907            color: var(--color-base);
908            margin-top: 4px;
909        }
910
911        :host([favorited=""]) svg, :host([favorited="true"]) svg {
912            fill: var(--color-notification);
913        }
914    </style>
915</template>
916<script>
917    class FavButtonWithCountElement extends FavButtonElement {
918
919        constructor() {
920            super();
921        }
922
923        // カスタム要素がDOMに接続された時に呼び出される
924        // constructorの直後に呼び出される
925        connectedCallback() {
926
927            // このElement(<fav-button-with-count>タグ)にShadow DOMをattachする
928            const shadowRoot = this.attachShadow({mode: 'open'});
929
930            // <template> タグの内容を複製し、Shadow DOMに追加
931            const template = document.getElementById('template-fav-button-with-count');
932
933            shadowRoot.appendChild(template.content.cloneNode(true));
934
935            // ボタンをクリックした時の挙動をセットする
936            this.setButtonEvent();
937
938            // お気に入り数を表示するspan要素を保持しておく
939            this.countElement = this.shadowRoot.querySelector('.count');
940
941            // お気に入り数の表示をセットする
942            this.setFavoriteCountText();
943        }
944
945        // 'favorited' 'favorite-count' attributeを切り替える
946        toggle() {
947            this.isFavorited = !this.isFavorited;
948            const count = parseInt(this.favoriteCount);
949            if (this.isFavorited) {
950                this.favoriteCount = (count + 1).toString();
951            } else {
952                this.favoriteCount = (count - 1).toString();
953            }
954        }
955
956        // span要素にcountを表示する
957        setFavoriteCountText() {
958            this.countElement.innerText = parseInt(this.favoriteCount).toLocaleString();
959        }
960
961        /*
962         * Attributes
963         */
964        get favoriteCount() {
965            return this.getAttribute('favorite-count');
966        }
967
968        set favoriteCount(val) {
969            this.setAttribute('favorite-count', val);
970            this.setFavoriteCountText();
971        }
972
973        get shouldOpenFavoriteListSelector() {
974            return true;
975        }
976
977    }
978
979    // <fav-button-with-count> タグの登録
980    customElements.define('fav-button-with-count', FavButtonWithCountElement);
981
982    window.addEventListener('tanp:favorites:sync', (event) => {
983        const detail = event.detail ?? {};
984        if (!detail.productId) {
985            return;
986        }
987        const targetProductId = detail.productId.toString();
988        const shouldFavorite = Boolean(detail.favorited);
989        FavButtonElement.favoriteButtonElements.forEach((element) => {
990            if (element.productId === targetProductId && element.isFavorited !== shouldFavorite) {
991                element.toggle();
992            }
993        });
994    });
995</script>
vendor: 70 bytes, lines 995-998
995
996
997
998<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
998GTM-54ST37K
vendor: 96 bytes, lines 998-1000
998" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
999
1000            
1000<header class="entryFormHeader">
1001        <div class="entryFormHeader__inner">
1002            <div class="entryFormHeaderContent">
1003                <a href="/">
1004                    <img class="entryFormHeaderContent__image"
1005                         src="https://images.tanp.jp/gracia.tanp/banners/logo/tanp_logo.png" alt="TANP(タンプ) ロゴ">
1006                </a>
1007            </div>
1008        </div>
1009    </header>
1010    <div class="wrapper">
1011                        <div class="cartPage">
1012    <div class="cartMain">
1013        <h1 class="cartMain__title">ショッピングカート</h1>
1014        <div class="cartMain__content">
1015                            <div class="cartPageEmptyContent">
1016                    <p class="cartPageEmptyContent__message">カート内に商品がありません。</p>
1017                    <a class="outlineButton" href="/">買い物を続ける</a>
1018                </div>
1019                    </div>
1020    </div>
1021    <div class="cartPageSection">
1022        <h2 class="cartPageSection__title">こちらも一緒にいかがですか?</h2>
1023        <div class="cartPageSection__content gridProducts">
1024            
1025    <product-item
1026        class="gtmImpression gtmProductClick "
1027        data-id="43996"
1028        product-id="43996"
1029                title="【お友達が色を選べる】ReFa AILE BRUSH (リファエールブラシ)"
1030        brand-title="ReFa(リファ)"
1031        product-group="ヘアブラシ"
1032        price="3800"
1033                            min-shipping-date="10月03日(土)"
1034                        image-url="https://images.tanp.jp/gracia.tanp/product43996/1600x1600-aile-brush-d1.jpg?w=560"
1035                            >
1036                            <fav-button
1037                slot="fav-button"
1038                product-id="43996"
1039                                                    show-modal-on-click
1040                                                log-add-fav-event="add_favorite_from_product_list"
1041                log-delete-fav-event="delete_favorite_from_product_list"
1042            >
1043            </fav-button>
1044            </product-item>
1045    <product-item
1046        class="gtmImpression gtmProductClick "
1047        data-id="38040"
1048        product-id="38040"
1049                title="限りなく天然温泉に近い入浴剤「HAA for bath 日々」"
1050        brand-title="HAA(ハー)"
1051        product-group="入浴剤"
1052        price="5280"
1053                            min-shipping-date="10月03日(土)"
1054                        image-url="https://images.tanp.jp/gracia.tanp/product38040/%E6%97%A5%E3%80%8510%E5%80%8B%E5%85%A5%E3%82%8A_TOP.jpg?w=560"
1055                            >
1056                            <fav-button
1057                slot="fav-button"
1058                product-id="38040"
1059                                                    show-modal-on-click
1060                                                log-add-fav-event="add_favorite_from_product_list"
1061                log-delete-fav-event="delete_favorite_from_product_list"
1062            >
1063            </fav-button>
1064            </product-item>
1065    <product-item
1066        class="gtmImpression gtmProductClick "
1067        data-id="11999"
1068        product-id="11999"
1069                title="【温熱&amp;EMS】ネックケア<NECK RELAX>"
1070        brand-title="NIPLUX(ニップラックス)"
1071        product-group="ボディケア機器"
1072        price="9680"
1073                            min-shipping-date="10月03日(土)"
1074                        image-url="https://images.tanp.jp/gracia.tanp/product11999/%25E3%2583%258D%25E3%2583%2583%25E3%2582%25AF%25E3%2582%25B1%25E3%2582%25A2.jpg?w=560"
1075                            >
1076                            <fav-button
1077                slot="fav-button"
1078                product-id="11999"
1079                                                    show-modal-on-click
1080                                                log-add-fav-event="add_favorite_from_product_list"
1081                log-delete-fav-event="delete_favorite_from_product_list"
1082            >
1083            </fav-button>
1084            </product-item>
1085    <product-item
1086        class="gtmImpression gtmProductClick "
1087        data-id="56837"
1088        product-id="56837"
1089                title="【全国約18,000施設から選べる】宿泊ギフトカード(ホテル・旅館)"
1090        brand-title="TANPカタログギフト"
1091        product-group="体験チケット・その他"
1092        price="10600"
1093                    should-display-price-range-mark
1094                            min-shipping-date="10月03日(土)"
1095                        image-url="https://images.tanp.jp/gracia.tanp/product56837/ChatGPT%20Image%202026%E5%B9%B47%E6%9C%8828%E6%97%A5%2017_33_26.png?w=560"
1096                            >
1097                            <fav-button
1098                slot="fav-button"
1099                product-id="56837"
1100                                                    show-modal-on-click
1101                                                log-add-fav-event="add_favorite_from_product_list"
1102                log-delete-fav-event="delete_favorite_from_product_list"
1103            >
1104            </fav-button>
1105            </product-item>
1106    <product-item
1107        class="gtmImpression gtmProductClick "
1108        data-id="42967"
1109        product-id="42967"
1110                title="【結婚祝い】カタログギフト &quot;Wedding&quot;【BLUE】"
1111        brand-title="TANPカタログギフト"
1112        product-group="ブランドカタログ・その他"
1113        price="9900"
1114                            min-shipping-date="10月03日(土)"
1115                        image-url="https://images.tanp.jp/gracia.tanp/product42967/ChatGPT%20Image%202026%E5%B9%B48%E6%9C%8812%E6%97%A5%2018_14_53.png?w=560"
1116                            >
1117                            <fav-button
1118                slot="fav-button"
1119                product-id="42967"
1120                                                    show-modal-on-click
1121                                                log-add-fav-event="add_favorite_from_product_list"
1122                log-delete-fav-event="delete_favorite_from_product_list"
1123            >
1124            </fav-button>
1125            </product-item>
1126        </div>
1127    </div>
1128    </div>
1129    </div>
1130
1131<input type="hidden" name="_csrfToken" value="Qlg5BybBCGlGWXQkcRZ8LstwaWtxV+CyVey+wBF/5T2AJdT3QroL5Z8Tujm+UY4J/x/3sIWRZp3sE6/sJZ0K2hlhOl1CNBen+RFZCRBkg4Az1Nz2dRqHBbYX4ZR22zjdqHGCW9hp72+ae5KJsqQC+Q=="/>
1132    
1133    <footer class="footer">
1134        <div class="footer__simple">
1135            <p class="footerCopyright__text">ネットでギフトを贈るなら | TANP(タンプ)</p>
1136            <p class="footerCopyright__text">Copyright© TANP Inc.</p>
1137        </div>
1138    </footer>
1139
1140<script src="https://assets.tanp.jp/72a9040/js/laravel-mix/dist/common/pages/Carts/index.js"></script>
1140<script src="https://assets.tanp.jp/72a9040/js/dist/pages/favorite_toast/index.js"></script>
1140</body>
1141</html>

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.