﻿# 如何利用 ChatGPT 有效率的將網站中所有 jQuery 轉換成 VanillaJS

我最近把[我的部落格](/)與[公司官網](https://www.duotify.com/)的 [jQuery](https://jquery.com/) 完整移除，全部都改用 [VanillaJS](http://vanilla-js.com) 來實現原本的功能，原本以為網站會需要改很多地方，但實際動手下去實現之後才發現其實也沒那麼難，只要你有強大的工具在手，這件工作其實很簡單，這篇文章我就來分享我的方法。

![AI](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/0ed7fd3dd5bb759bab1e-214613064-771a74e7-345d-4622-bf90-398fd9a5d6a3.jpg)

我的方法真的超簡單，就是使用 [ChatGPT](https://chat.openai.com/chat/) 來轉換，你只要學習一些「**咒語**」就可以讓這個 AI 人工智能引擎幫你寫 Code，真的不誇張，我到目前為止，轉換成功率高達 98%，幾乎沒有寫錯的時候！👍

以下是我轉換過的幾個範例：

1.  自動隱藏我的部落格右側的內容
    
    我的部落格一直有個隱藏版的密技，在網頁開啟後按下 `f` 或 `Ctrl+B` 就可以隱藏部落格右側的內容，讓文章可以「全版面」檢視，提升閱讀體驗，其中最核心的程式碼如下，有用到一點點 jQuery，難度不高：
    
    ```javascript
    window.toggleAside = (e) => {
      if (e) e.preventDefault();
      const currentUrl = new URL(window.location.href);
      const searchParams = new URLSearchParams(currentUrl.search);
      if (jQuery('section.content-wrapper').hasClass('col-md-8')) {
        searchParams.set('full', '1');
        currentUrl.search = searchParams.toString();
        window.history.replaceState(null, '', currentUrl.toString());
        jQuery('aside').hide()
        jQuery('section.content-wrapper').removeClass('col-md-8').addClass('col-md-12')
      } else {
        searchParams.delete('full');
        currentUrl.search = searchParams.toString();
        window.history.replaceState(null, '', currentUrl.toString());
        jQuery('section.content-wrapper').removeClass('col-md-12').addClass('col-md-8')
        jQuery('aside').show()
      }
    }
    ```
    
    我的咒語是：
    
    ```
    請幫我把這段程式碼的 jQuery 程式碼都改成 VanillaJS，不用解釋，給我 code 就好，也請順便幫我加上註解：
    
    <<貼上你的程式碼>>
    ```
    
    結果如下：
    
    ```javascript
    window.toggleAside = (e) => {
      // Prevent the default action of the event
      if (e) e.preventDefault();
    
      // Get the current URL and its search parameters
      const currentUrl = new URL(window.location.href);
      const searchParams = new URLSearchParams(currentUrl.search);
    
      // Select the content wrapper and aside elements
      const contentWrapper = document.querySelector("section.content-wrapper");
      const aside = document.querySelector("aside");
    
      // Check if the content wrapper has the class "col-md-8"
      if (contentWrapper.classList.contains("col-md-8")) {
        // Add the "full" parameter to the URL
        searchParams.set("full", "1");
        currentUrl.search = searchParams.toString();
        window.history.replaceState(null, "", currentUrl.toString());
    
        // Hide the aside element and update the content wrapper's class
        aside.style.display = "none";
        contentWrapper.classList.remove("col-md-8");
        contentWrapper.classList.add("col-md-12");
      } else {
        // Remove the "full" parameter from the URL
        searchParams.delete("full");
        currentUrl.search = searchParams.toString();
        window.history.replaceState(null, "", currentUrl.toString());
    
        // Show the aside element and update the content wrapper's class
        aside.style.display = "block";
        contentWrapper.classList.remove("col-md-12");
        contentWrapper.classList.add("col-md-8");
      }
    };
    ```
    
    > 他是真的看的懂 jQuery 程式碼！🔥
    
2.  透過 `$.ajax` 送出表單資料到後端
    
    我[公司官網](https://www.duotify.com/)可以透過每頁下方的「聯絡我們」留言給我們，其表單送出是透過 jQuery 的 `$.ajax` 完成的，程式碼如下：
    
    ```javascript
    $("#mainContactForm").submit(function (event) {
        var area = document.querySelector(".contact-fixed-container");
        event.preventDefault();
        updateRecaptchaToken((token) => {
            $.ajax({
                url: "/contact",
                method: "post",
                data: $('#mainContactForm').serialize(),
                processData: false,
                contentType: false,
                success: function (res) {
                    area.classList.add("active");
                    setTimeout(() => {
                        area.classList.remove("active");
                    }, "1000");
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    alert(jqXHR.responseJSON.message);
                }
            });
        });
    });
    ```
    
    我的咒語是：
    
    ```
    請幫我把這段程式碼的 jQuery 程式碼都改成 VanillaJS，其中 $.ajax 的部分改用 fetch API，後端回應如果成功就不用解析結果，如果失敗，要解析 JSON 內容，其中 alert 的部分改用 SweetAlert2，不用解釋，給我 code 就好，也請順便幫我加上註解：
    
    <<貼上你的程式碼>>
    ```
    
    結果如下：
    
    ```javascript
    // VanillaJS equivalent of jQuery's submit function
    document.querySelector("#mainContactForm").addEventListener("submit", function(event) {
      var area = document.querySelector(".contact-fixed-container");
      event.preventDefault();
      updateRecaptchaToken((token) => {
        // Use fetch API to make a POST request
        fetch("/contact", {
          method: "post",
          body: new FormData(document.querySelector("#mainContactForm")),
          processData: false,
          contentType: false
        })
        .then(response => {
          // Check if the request is successful
          if (!response.ok) {
            throw response;
          }
          area.classList.add("active");
          setTimeout(() => {
            area.classList.remove("active");
          }, 1000);
        })
        .catch(error => {
          // If request fails, parse the error message from the response
          error.json().then(errorJson => {
            // Use SweetAlert2 to display the error message
            Swal.fire({
              title: 'Error',
              text: errorJson.message,
              icon: 'error',
            });
          });
        });
      });
    });
    ```
    
    由於我的後端程式有判斷 `X-requested-with: XMLHttpRequest` 標頭(HTTP Header)，用以識別前端傳來的內容是否為 AJAX 的要求，不過這段 fetch API 呼叫並沒有處理這個部分，所以我再次輸入一個新的「咒語」如下：
    
    ```
    幫我加上 x-requested-with: XMLHttpRequest 標頭
    ```
    
    > ChatGPT 因為可以記得我曾經輸入過的前後文，所以我不用再次輸入原始碼，他會直接給我一份新的程式碼版本。
    
    ```javascript
    // VanillaJS equivalent of jQuery's submit function
    document.querySelector("#mainContactForm").addEventListener("submit", function(event) {
      var area = document.querySelector(".contact-fixed-container");
      event.preventDefault();
      updateRecaptchaToken((token) => {
        // Use fetch API to make a POST request
        fetch("/contact", {
          method: "post",
          body: new FormData(document.querySelector("#mainContactForm")),
          headers: { 'X-Requested-With': 'XMLHttpRequest' },
          processData: false,
          contentType: false
        })
        .then(response => {
          // Check if the request is successful
          if (!response.ok) {
            throw response;
          }
          area.classList.add("active");
          setTimeout(() => {
            area.classList.remove("active");
          }, 1000);
        })
        .catch(error => {
          // If request fails, parse the error message from the response
          error.json().then(errorJson => {
            // Use SweetAlert2 to display the error message
            Swal.fire({
              title: 'Error',
              text: errorJson.message,
              icon: 'error',
            });
          });
        });
      });
    });
    ```
    
    上面這段 Code 很聰明的幫我改用 `FormData` 取得表單資料，而且當中有一個我自己寫的 `updateRecaptchaToken` 函式呼叫，他直接透過我的命名判斷出了一個相對正確的註解(命名很重要)。不過，這段程式還是有些問題，其中 `processData: false` 與 `contentType: false` 這兩個參數是錯誤的，這是 jQuery 專屬的選項設定，fetch API 並沒有。所以我再下一次新的咒語：
    
    ```
    請移除 processData: false 與 contentType: false 參數，這兩個參數不在 fetch API 之中
    ```
    
    我的天，程式碼 100% 正確！
    
    ```javascript
    // VanillaJS equivalent of jQuery's submit function
    document.querySelector("#mainContactForm").addEventListener("submit", function(event) {
      var area = document.querySelector(".contact-fixed-container");
      event.preventDefault();
      updateRecaptchaToken((token) => {
        // Use fetch API to make a POST request
        fetch("/contact", {
          method: "post",
          body: new FormData(document.querySelector("#mainContactForm")),
          headers: { 'X-Requested-With': 'XMLHttpRequest' },
        })
        .then(response => {
          // Check if the request is successful
          if (!response.ok) {
            throw response;
          }
          area.classList.add("active");
          setTimeout(() => {
            area.classList.remove("active");
          }, 1000);
        })
        .catch(error => {
          // If request fails, parse the error message from the response
          error.json().then(errorJson => {
            // Use SweetAlert2 to display the error message
            Swal.fire({
              title: 'Error',
              text: errorJson.message,
              icon: 'error',
            });
          });
        });
      });
    });
    ```
    

透過上述兩個簡短的例子，相信你也跟我一樣，有被 ChatGPT 的語言模型震懾到，雖說 ChatGPT 目前還沒辦法真的做到 100% 轉換，有些錯誤的地方還是需要人工判斷，且就算相同的「咒語」輸入，他也不見得會回覆你完全相同的結果，但其實這份程式碼還是對我完成「轉換」任務有相當大的幫助，節省了大量的時間！👍

國外最近一個月開始有了個新職缺叫做 [`Prompt Engineer`](https://en.wikipedia.org/wiki/Prompt_engineering) (提示工程師?)，這個工作，就是讓「人類」跟 AI 溝通的專家，透過各種「提示」文字，我稱之為「咒語」，讓 AI 幫我們工作，而且是一些創造性的工作，非常厲害！🔥

但我覺得目前的 `Generative AI` (可以產生創造性工作的 AI 機器人) 真正能給軟體開發人員帶來的效益，還是那些會下「咒語」的「資深工程師」，如果你現在還處於初學狀態，其實要下出一個優質的咒語其實沒那麼容易。不過，學習下「咒語」絕對比學會「程式設計」來的簡單，對吧？所以不久的將來「軟體開發圈」會變成什麼樣子，老實說我很難預測，但更大幅度的 M 型社會肯定很快就會來臨，善用不同的工具來解決問題，才能站在巨人的肩膀上持續創造價值！🚀

### 相關連結

-   [jQuery](https://jquery.com/)
-   [fetch() - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
-   [Handling requests with fetch - DEV Community 👩‍💻👨‍💻](https://dev.to/sarahob/that-s-so-fetch-4fo3)
-   [How to go deeper with ChatGPT using prompt engineering](https://technical.ly/software-development/prompt-enginnering-chatgpt-dall-e-openai/)
-   [Prompt Engineering Guide](https://github.com/dair-ai/Prompt-Engineering-Guide)
