trackProduct

  1. tracker(‘viewItem’, product)
  2. tracker(‘addToCart’, product)
  3. tracker(‘addToWishlist’, product)

程式碼的觸發

const trackProduct = new Event({
  name: 'trackProduct',
  trigger: routeChange.path.contains('/product').and(routeChange.host.eq('www.eslite.com')),
  runOnce: false,
  delay: 2000,
  fn: function (retry = 5) {
    let product
    let method = 'update'
    // 這個 ecName 是從別的地方匯過來的,就是商家姓名 - sku 就是產品的獨特ID
    const sku = `${ecName}:product:${location.pathname.match(/product\/(\d+)/).pop()}`
    logger(sku, "==========")
    // 這一段不太懂
    if (document.querySelector('.nuxt-error')?.textContent.match('頁面不存在|程式錯誤500')) {
      method = 'expire'
      product = new Product({ sku, live: 0 })
      // insertPixel 就是一個插入一段程式碼的 fc,後面接的就是要插入的程式碼,這邊插入的應該就是 塔圖的追蹤碼
      insertPixel(`//track.tagtoo.com.tw/up?t=${method.slice(0, 1)}&${product.toQueryString()}`)      
      return
    }
    // 商品名稱
    const title = document.querySelector('h1')?.textContent
    // 特價
    const price = document.querySelector('span.item-price')?.textContent.replace(/\D/g, '')
    // 原價
    const storePrice = document.querySelector('span.pre-product-price')?.textContent.replace(/\D/g, '') ?? price
    // 商品描述 -> 他把其他怪怪的字符都用空字串取代掉了,最後只取前200個字
    const description =
      document
        .querySelector('.article')
        ?.textContent.replace(/<[^>]+>|[\uffff]/g, '')
        .slice(0, 200) ?? ''
    // 書本規格 -> 判斷這本書是否為限制級的書籍
    const restricted = [...document.querySelectorAll('.product-description-schema .row .row span')].some((e) =>
      e.textContent.match('級別.*限')
    )
    // 如果是就回傳,不是就繼續往下
    if (restricted) return
    // 麵包屑
    const categoryPath = Array.from(document.querySelectorAll('.breadcrumb li'), ($li) => $li.textContent).join(
      '>'
    )
    // 圖片網址
    const imageUrl = document.querySelector('.product-images .swiper-slide-active img')?.src
    // 購買按鈕
    const live = Number(
      document
        .querySelector('.product-information .btn-eslite-red, .btn-secondary')
        ?.textContent.includes('直接購買')
    )
    // btn-eslite-red: 直接購買/貨到通知
    // btn-secondary: 無法購買

    // 作者
    const author = document.querySelector('.author span.text-underline')?.textContent ?? ''
    // 出版社
    const publisher = document.querySelector('.publisher span.text-underline')?.textContent ?? ''
    // 規格
    const schema = document.querySelector('.product-description-schema')?.textContent ?? ''
    // ISBN 號碼
    const isbn = schema.match(/ISBN13 \/ (\d+)/)?.pop() ?? ''
    // 分開符號
    const separator = ' ' + '' + ' '
    // 活動口號
    const promotionTag = Array.from(document.querySelectorAll('.promotion-activities-items > .row'))
      .map((e) => e.textContent.trim())
      .join(separator)
    // 活動口號句尾加上一段字
    const tags =
      Array.from(document.querySelectorAll('.tags .badge'), (e) => {
        return e.textContent.trim()
      }).join(separator) + `-promotionTag:${promotionTag}`
    // 活動口號開頭再加上另一段推行銷字串
    const activity = Array.from(
      document.querySelectorAll('.promotion-activities-items > .row , .exhibition-name'),
      (e) => {
        return e.textContent.trim()
      }
    ).join(separator)
    // 運費
    const shipping = 50
    // 產品的細節 - 包含客製化抓的東西
    product = new Product({
      title,
      sku,
      price,
      storePrice,
      description,
      categoryPath,
      imageUrl,
      live,
      customLabel1: author,
      customLabel2: publisher,
      customLabel3: tags,
      customLabel4: activity,
      shipping,
      gtin: isbn,
    })
    
    logger(isNaN(product.get('price')), product.get('imageUrl').match('/img/loading_'))
    // 如果今天產品沒有原價、沒有特價、沒有圖片,持續嘗試爬蟲,我們一開始有設定retry = 5 ,所以可以跑5次迴圈,又或者是,書籍的類型包含 性知識/性愛,就回傳null
    if (
      isNaN(product.get('price')) ||
      isNaN(product.get('storePrice')) ||
      product.get('imageUrl').match('/img/loading_')
    ) {
      setTimeout(() => {
        if (retry > 0) {
          retry--
          window.tgk('run', 'trackProduct', retry)
        }
      }, 1000)
      return null
    } else if (categoryPath.match('性知識/性愛')) {
      return null
    }
    // 送出  viewItem 事件
    tracker('viewItem', product) 
    // 插入像素
    insertPixel(`//track.tagtoo.com.tw/up?t=${method.slice(0, 1)}&${product.toQueryString()}`)
    logger(insertPixel(`//track.tagtoo.com.tw/up?t=${method.slice(0, 1)}&${product.toQueryString()}`), "=========")

    // 微軟的追蹤碼
    let UETData = ['ecommerce', 'ViewContent', product.get('sku'), product.get('price')]
    sendUETCustomEvent(...UETData)

    // 加入購物車和直接購買的按鈕
    const $addToCartBtns = document.querySelectorAll('div.add-cart.cart-button, div.direct-buy')    
    $addToCartBtns.forEach(($btn) => {
      $btn.addEventListener('click', () => {
        // 兩個按鈕點擊,都要送 addToCart 事件
        tracker('addToCart', product)
        // 送微軟事件
        UETData = ['ecommerce', 'AddToCart', product.get('sku'), product.get('price')]
        sendUETCustomEvent(...UETData)
      })
    })
    // 蒐藏按鈕
    const $addToWishlistBtn = document.querySelector('div.add-trace.cart-button')
    $addToWishlistBtn?.addEventListener('click', () => {
      // 送 addToWishlist 事件
      tracker('addToWishlist', product)
      // 送微軟事件
      UETData = ['ecommerce', 'AddToWishlist', product.get('sku'), product.get('price')]
      sendUETCustomEvent(...UETData)
    })
  },
})

印出來的 console

> fire all viewItem (3ad66d57-42a8-48a9-8280-f525d9f30569)
>Product(17) {'advertiserId' => 1655, 'title' => '【藥師健生活】上班必備組(五百益生菌x1+葉黃素x1) 共2入', 'sku' => 'eslite:product:100', 'link' => 'https://www.eslite.com/product/100F339042682337476000', 'categoryPath' => '首頁>食品保健>養生保健>益生菌', …}
> fire facebook viewItem event
> send gtag event 'view_item' to ["AW-477262169"]
> {items: Array(1), value: 2790, currency: 'TWD', send_to: Array(1)}
> fire google viewItem event
> tagtoo.tracker.event(): ViewContent, [object Object]
> fire tagtoo viewItem event
> fire yahoo viewItem event

trackCheckout

  1. tracker(‘trackCart’, cart)
  2. tracker(‘checkout’, cart)

程式碼的觸發

const trackCheckout = new Event({
  name: 'trackCheckout',
  // 網址包含 /cart/step1 , 就觸發事件
  trigger: routeChange.path.contains('/cart/step1').and(routeChange.host.eq('www.eslite.com')),
  delay: 3000,
  runOnce: false,
  fn: (retry = 5) => {
    // 宣告一個函式
    const getCart = () => {
      // cart 變數,繼承 Cart 物件
      const cart = new Cart()
      // 抓到購物車所有的產品
      const $items = document.querySelectorAll('div.cart-item-row')
      // forEach 迴圈抓資料
      $items.forEach(($item) => {
        // 標題
        const title = $item.querySelector('.product-name').textContent
        // 產品ID
        const pID = $item
          .querySelector('a')
          .href.match(/product\/(\d+)/)
          .pop()
        // 有前綴的產品獨特ID
        const sku = `${ecName}:product:${pID}`
        // 產品價格
        const price = $item.querySelector('.cart-price-black').textContent.replace(',', '')
        // 數量
        const qty = $item.querySelector('input.number-view').value
        // 把剛剛的東西都塞進 cart 裡面
        cart.add(new Product({ title, sku, price, qty }))
      })
      // 儲存這些資訊到 cart
      cart.save()
      return cart
    }

    // 點選結帳
    const nextStepBtn = document.querySelector('.summary-item .btn-e-red')

    // 如果這個事件已經綁定過了,就直接回傳
    if (nextStepBtn._tg_event_bind) {
      return
    }

    // 檢查購物車是不是空的,如果是空的,等幾秒後再檢查一次,檢查五次後還是空的話,再結束動作
    const cart = getCart()
    if (cart.products.length === 0) {
      setTimeout(() => {
        if (retry > 0) {
          retry--
          window.tgk('run', 'trackCheckout', retry)
        }
      }, 1500)
      return
    }

    // 送出兩個 tracker
    tracker('trackCart', cart)
    tracker('checkout', cart)

    // 整理成 Yahoo 渠道格式
    const cartData = cart.toYahoo()
    // 送到微軟渠道
    sendUETCustomEvent('ecommerce', 'AddToCart', cartData.skus, cartData.value)
    sendUETCustomEvent('ecommerce', 'InitiateCheckout', cartData.skus, cartData.value)

    // 點擊按鈕就觸發 getCart()
    nextStepBtn.addEventListener('click', () => {
      getCart()
    })

    // 綁定事件,這樣不會重複觸發
    nextStepBtn._tg_event_bind = true
  },
})

印出來的 console

> fire all trackCart (4ab2ee8f-51b7-408a-8018-6cfc31afc391)
> Cart {products: Array(2), total: 4180, tax: 0, shipping: 0, currency: undefined, …}
> fire facebook trackCart event
> send gtag event 'view_cart' to ["AW-477262169"]
> {transaction_id: undefined, value: 4180, shipping: 0, currency: 'TWD', coupon: undefined, …}
> fire google trackCart event
> tagtoo.tracker.event(): AddToCart, [object Object]
> fire tagtoo trackCart event
> fire yahoo trackCart event
> fire all checkout (eb9a578e-5589-4171-8e13-5721a49f98f1)
> Cart {products: Array(2), total: 4180, tax: 0, shipping: 0, currency: undefined, …}
> fire facebook checkout event
> send gtag event 'begin_checkout' to ["AW-477262169"]
> {transaction_id: undefined, value: 4180, shipping: 0, currency: 'TWD', coupon: undefined, …}
> fire google checkout event
> fire yahoo checkout event
> fire facebook checkout conversion
> send gtag event 'conversion' to "AW-477262169/oJzUCMbUq-sBENniyeMB"
> {send_to: 'AW-477262169/oJzUCMbUq-sBENniyeMB', value: 4180, currency: 'TWD', transaction_id: 'ecc12fe2-54ae-47a1-9847-e472dc289e74'}
> fire google checkout conversion
> fire tagtoo checkout conversion
> fire yahoo checkout conversion

trackPayment

  1. tracker(‘addShippingInfo’, cart)

程式碼的觸發

// 付款方式事件
const trackPayment = new Event({
  name: 'trackPayment',
  // 網只有 step2 觸發事件
  trigger: routeChange.path.contains('/cart/step2').and(routeChange.host.eq('www.eslite.com')),
  delay: 2000,
  runOnce: true,
  fn: () => {
    // 宣告一個函式 - 購物車物流
    const trackShippingInfo = () => {
      // 讀取我們剛剛存擋的購物車
      const cart = Cart.load()
      // 宣告物流的方法
      const methods = {
        '7-ELEVEN': '711',
        全家: 'cvs',
        宅配: 'drop_shipping',
        門市: 'in_store',
        以外: 'drop_shipping',
        捐書: 'drop_shipping',
      }
      // 收取貨物的方法
      const selectedShipping = document.querySelector('.cart-options-block .radio-option.active').title
      // 購物車的運送方式 = 這一段的意思就是要用 selectedShipping 這個變數,找到 methods 對應的 key 值
      cart.shippingMethod = Object.entries(methods).find(([method]) => selectedShipping.includes(method))[1]
      // 送出tracker
      tracker('addShippingInfo', cart)
    }
    // 宣告一個函式 - 購物車金流
    const trackPaymentInfo = () => {
      // 讀取購物車資料
      const cart = Cart.load()
      // 抓到選擇的金流方式
      cart.payment = document.querySelector('#step2-box-payment .radio-option.active').title
      // 送出tracker
      tracker('addPaymentInfo', cart)
      // 購物車儲存
      cart.save()

      // yahoo 渠道格式
      const cartData = cart.toYahoo()
      // 送一份到微軟
      sendUETCustomEvent('ecommerce', 'AddPaymentInfo', cartData.skus, cartData.value)
    }
    // 確認結帳按鈕
    const $placeOrderButton = document.querySelector('.summary-item button[type="submit"]')
    // 判斷按鈕一開始有沒有綁定事件,沒有的話一開始false
    if ($placeOrderButton && !$placeOrderButton._tg_event_bind) {
      $placeOrderButton.addEventListener('click', () => {
        // 觸發兩個tracker
        trackShippingInfo()
        trackPaymentInfo()
      })
      // 綁定事件
      $placeOrderButton._tg_event_bind = true
    }
  },
})

印出來的 console

> fire all addShippingInfo (9b7a6513-deab-40c8-a210-91c275ca5a49)
> Cart {products: Array(1), total: 379, tax: 0, shipping: 0, currency: undefined, …}
> send gtag event 'add_shipping_info' to ["AW-477262169"]
> {transaction_id: undefined, value: 379, shipping: 0, currency: 'TWD', coupon: undefined, …}
> fire google addShippingInfo event
> fire tagtoo addShippingInfo event

trackPurchase

  1. tracker(‘purchase’, cart)

程式碼的觸發

// 完成購買事件
const trackPurchase = new Event({
  name: 'trackPurchase',
  // 網址有 step3 觸發事件
  trigger: routeChange.path.contains('/cart/step3').and(routeChange.host.eq('www.eslite.com')),
  delay: 1500,
  fn: () => {
    // 宣告變數
    const storageKeyName = '_sent_trans_ids'
    // 儲存商店ID
    const storeIds = storage.get(storageKeyName, [])
    // 儲存訂單ID - 完成訂單的時候,網址會有有該筆訂單的ID,把他抓下來
    const orderId = location.search.match(/orderid=(\d+)/).pop()
    
    // 如果今天商店ID裡面沒有訂單的ID,那就做以下的事情
    if (storeIds.indexOf(orderId) === -1) {
      // 讀取購物車
      const cart = Cart.load()
      // 設定購物車的訂單ID
      cart.orderId = orderId
      // 送出 tracker
      tracker('purchase', cart)

      // yahoo 渠道格式
      const cartData = cart.toYahoo()
      // 送一份到微軟
      sendUETCustomEvent('ecommerce', 'Purchase', cartData.skus, cartData.value)

      // 購物車清空
      Cart.clear()
      // 把訂單ID塞進商店ID裡面
      storeIds.push(orderId)
      // storage.set 是將程式碼存在瀏覽器的方式,這一段是將 storeIds 陣列以 storageKeyName 變數名存在瀏覽器中,有效期是30小時
      storage.set(storageKeyName, storeIds, 60 * 60 * 30)
    }
  },
})

印出來的 console

> fire all purchase (f458da53-2030-496d-bb06-fce059fad465)
> Cart {products: Array(2), total: 1213, tax: 0, shipping: 0, currency: undefined, …}
> fire facebook purchase event
> send gtag event 'purchase' to ["AW-477262169"]
> {transaction_id: '2398315', value: 1213, shipping: 0, currency: 'TWD', coupon: undefined, …}
> fire google purchase event
> tagtoo.tracker.event(): Purchase, [object Object]
> fire tagtoo purchase event
> fire yahoo purchase event
> fire facebook purchase conversion
> send gtag event 'conversion' to "AW-477262169/9friCJnMkOkBENniyeMB"
> {send_to: 'AW-477262169/9friCJnMkOkBENniyeMB', value: 1213, currency: 'TWD', transaction_id: '2398315'}
> fire google purchase conversion
> fire tagtoo purchase conversion
> fire yahoo purchase conversion

trackRegister

  1. tracker(‘register’, ‘phone’)
  2. sendUETCustomEvent(‘ecommerce’, ‘Register’, ‘phone’, 1)

程式碼的觸發

const trackRegister = new Event({
  name: 'trackRegister',
  trigger: routeChange.path.contains('/registry/complete').and(routeChange.host.eq('www.eslite.com')),
  delay: 1500,
  fn: () => {
    tracker('register', 'phone')
    sendUETCustomEvent('ecommerce', 'Register', 'phone', 1)
  },
})

印出來的 console

> fire all register (694ffe23-987e-4be7-9d11-86ed0f2cbde4)
> phone
> fire facebook register event
> send gtag event 'sign_up' to ["AW-477262169"]
> {method: 'phone', send_to: Array(1), currency: 'TWD'}currency: "TWD"method: "phone"send_to: ['AW-477262169'][[Prototype]]: Object
> fire google register event
> fire yahoo register event
> fire facebook register conversion
> send gtag event 'conversion' to "AW-477262169/MZErCN7juesBENniyeMB"
> {send_to: 'AW-477262169/MZErCN7juesBENniyeMB', value: undefined, currency: 'TWD', transaction_id: '96914b6e-5786-4e61-b690-05b00635f03e'}
> fire google register conversion
> fire tagtoo register conversion
> fire yahoo register conversion

trackLogin

  1. tracker(‘login’, { label: ‘phone’ })

程式碼觸發

// 登入事件
const trackLogin = new Event({
  name: 'trackLogin',
  // 網址有login會觸發
  trigger: routeChange.path.contains('/login').and(routeChange.host.eq('www.eslite.com')),
  delay: 1500,
  fn: () => {
    // 找到按鈕
    const $loginBtn = document.querySelector('button')
    logger($loginBtn._tg_event_bind, "========")
    // 綁定事件 - 一開始因為沒綁定事件,先讓按鈕的事件是false,並用!來讓他變成true
    if ($loginBtn && !$loginBtn._tg_event_bind) {
      // 按下按鈕的時候
      $loginBtn.addEventListener('click', () => {
        // 宣告變數
        let isValid = true
        // 登入的兩個欄位
        const $formInputs = document.querySelectorAll('input.form-control')
        // 兩個欄位跑迴圈,如果其中一個欄位沒有填寫,就把 isValid 設定為 false
        $formInputs.forEach(function ($input) {
          if ($input.value.length === 0) {
            isValid = false
          }
        })
        // isValid 要是 true 才送 tracker
        if (isValid) {
          tracker('login', { label: 'phone' })
        }
      })
      // 點擊按鈕後,綁定事件
      $loginBtn._tg_event_bind = true
    }
  },
})

印出來的 console

> fire all login (d4fb832b-827c-4a4d-b0ce-7c47c0e7ca99)
> {label: 'phone'}
> send gtag event 'login' to ["AW-477262169"]
> {method: '[object Object]', send_to: Array(1), currency: 'TWD'}
> fire google login event

trackSearch

  1. tracker(‘search’, searchKey)

程式碼觸發

// 搜尋事件
const trackSearch = new Event({
  name: 'trackSearch',
  // 網址有 Search 的時候,會觸發事件
  trigger: routeChange.path.contains('/Search').and(routeChange.host.eq('www.eslite.com')),
  delay: 1500,
  fn: () => {
    // 宣告變數
    const $titleTag = document.querySelector('title')
    // 確認之前是否有綁定事件
    if (!$titleTag._tg_event_bind) {
      // 這一段的意思是,會解析網站的連結,location.search 會找到 search 後面的部分,.match(/keyword=([^&]+)/) 是正規表達式,找到keyword的詞
      const searchKey = decodeURIComponent(location.search.match(/keyword=([^&]+)/).pop())
      // 送出 tracker 事件
      tracker('search', searchKey)
      // 送出微軟事件
      sendUETCustomEvent('ecommerce', 'Search', searchKey, 1)
    }
    // 綁定事件
    $titleTag._tg_event_bind = true
  },
})

印出來的 console

> fire all search (114e6490-ea43-4510-8bfc-5294856bb351)
> 歷史
> fire facebook search event
> send gtag event 'search' to ["AW-477262169"]
> {search_term: '歷史', send_to: Array(1), currency: 'TWD'}
> fire google search event
> fire yahoo search event
> fire facebook search conversion
> send gtag event 'conversion' to "AW-477262169/Fb2QCL_uq-sBENniyeMB"
> {send_to: 'AW-477262169/Fb2QCL_uq-sBENniyeMB', value: undefined, currency: 'TWD', transaction_id: '69c2fec9-a97a-462c-b160-2930cca4741c'}
> fire google search conversion
> fire tagtoo search conversion
> fire yahoo search conversion

trackExhibitions

  1. tracker(addToCart, new Product({ title, sku, price }))
  2. tracker(addToWishlist, new Product({ title, sku, price }))

程式碼觸發

// 觸發事件
const trackExhibitions = new Event({
  name: 'trackExhibitions',
  // 觸發連結
  trigger: routeChange.path.contains('/exhibitions'),
  delay: 1500,
  fn: () => {
    // 宣告一個函式 - 這個函式會抓取點到的東西
    const bindTrackingEvent = (e) => {
      const target = e.target
      const $item = target.closest('div.card')
      const title = $item.querySelector('.desc').title
      const pID = $item.querySelector('a').href.split('/').pop()
      const sku = `${ecName}:product:${pID}`
      const price = $item.querySelector('.slider-price').textContent.replace(/\D/g, '')
      tracker(e.currentTarget._tg_event_name, new Product({ title, sku, price }))
    }
    // 加入購物車按鈕
    const $addToCartBtns = document.querySelectorAll('.small-cart-and-wish-btn button:first-of-type, .cart-btn')
    // 家務許願清單按鈕
    const $addToWishlistBtns = document.querySelectorAll('.wish-btn, .small-cart-and-wish-btn button:nth-child(2)')
    $addToCartBtns.forEach(($btn) => {
      // 判斷目前有沒有綁定事件
      if (!$btn._tg_event_name) {
        // 綁定 addToCart 事件
        $btn._tg_event_name = 'addToCart'
        // 點擊按鈕、觸發上面的函式
        $btn.addEventListener('click', bindTrackingEvent)
      }
    })
    $addToWishlistBtns.forEach(($btn) => {
      if (!$btn._tg_event_name ) {
        // 綁定 addToWishlist 事件
        $btn._tg_event_name = 'addToWishlist'
        // 點擊按鈕、觸發上面的函式
        $btn.addEventListener('click', bindTrackingEvent)
      }
    })
  },
})

印出來的 console

加入許願清單

> fire all addToWishlist (e3b194db-47b2-4c02-948f-0ab0ca8b2d23)
> Product(7) {'advertiserId' => 1655, 'title' => "I'm So Happy You're Here: A Little Book About Why You're Great", 'sku' => 'eslite:product:1001283452682184591000', 'link' => 'https://www.eslite.com/exhibitions/CU202011-01219', 'price' => 299, …}
> fire facebook addToWishlist event
> send gtag event 'add_to_wishlist' to ["AW-477262169"]
> {items: Array(1), value: 299, currency: 'TWD', send_to: Array(1)}
> fire google addToWishlist event
> fire yahoo addToWishlist event

加入購物車

> fire all addToCart (47bccdd6-178e-49a9-880a-2fccbd64fa64)
> Product(7) {'advertiserId' => 1655, 'title' => "I'm So Happy You're Here: A Little Book About Why You're Great", 'sku' => 'eslite:product:1001283452682184591000', 'link' => 'https://www.eslite.com/exhibitions/CU202011-01219', 'price' => 299, …}
> fire facebook addToCart event
> send gtag event 'add_to_cart' to ["AW-477262169"]
> {items: Array(1), value: 299, currency: 'TWD', send_to: Array(1)}
> fire google addToCart event
> tagtoo.tracker.event(): AddToCart, [object Object]
> fire tagtoo addToCart event
> fire yahoo addToCart event
> fire facebook addToCart conversion
> send gtag event 'conversion' to "AW-477262169/LU6WCOfUuesBENniyeMB"
> {send_to: 'AW-477262169/LU6WCOfUuesBENniyeMB', value: 299, currency: 'TWD', transaction_id: 'd75d7a32-db52-45f0-a7d3-166d74792876'}
> fire google addToCart conversion
> fire tagtoo addToCart conversion
> fire yahoo addToCart conversion