/ JavaScript程式

透過 Ajax 在不換頁的情況下提升作品質感

不管你是用 PHP、JSP(Servlet)還是在寫純 HTML,如果你還在寫 <a href="foo.html">bar</a> 的話,你會發現你每次點下去都會跳到新的頁面。要怎麼點下去跑出 loading 動畫,然後顯示新的資訊呢?

原本你可能會寫出這樣的東西:

<!-- mail.html -->
<form action="send.php" method="post">
    <input type="email" name="email" placeholder="收件人信箱" />
    <input type="text" name="name" placeholder="收件人姓名" />
    <input type="text" name="subject" placeholder="主旨" />
    <textarea name="body" placeholder="內容"></textarea>
    <input type="submit" value="傳送" />
</form>
<?php
/* send.php */
require 'PHPMailerAutoload.php';

$email = $_POST['email'];
$name = $_POST['name'];
$subject = $_POST['subject'];
$body = $_POST['body'];

$mail = new PHPMailer;
$mail->setFrom('from@example.com', 'Your Name');
$mail->addAddress($email, $name);
$mail->Subject  = $subject;
$mail->Body     = $body;
if(!$mail->send()) {
  echo 'Message was not sent.';
  echo 'Mailer error: ' . $mail->ErrorInfo;
} else {
  echo 'Message has been sent.';
}

然後你會發現,你送出表單後整個畫面會先變白,跳到 send.php。然後等信件送出去後,跑出乾乾的一句話:Message has been sent.

為什麼 Gmail 可以做到免跳轉就把 mail 送出去?還能跳出一個小通知告訴你信件已經寄出。我們也能做到類似的事情嗎?

透過 Ajax 發送 request 和後端溝通

如果你想要不離開這個頁面的話,就必須透過 Ajax。Ajax 可以讓你不離開目前的頁面,把請求傳送到某個頁面,再把回應傳回來。你可以依照回應來做指定的動作。

以上面的例子來說,我們應該要在 mail.html 發送請求到 send.php,再依照 send.php 回傳的回應來決定要做什麼動作。而 send.php 在發送信件時只有成功和失敗兩種回應。

回應的部分我們會用 JSON 格式來回傳,所以我們要先把原本的 PHP echo 的字串改成 json 格式:

/* send.php */
if(!$mail->send()) {
      echo '{"success": false}';
} else {
      echo '{"success": true}';
}

使用 jQuery 來做 Ajax 請求

回到前端做 Ajax。實作 Ajax 最簡單的方法應該是使用 jQuery,首先在 html 裡面引入最新的 jQuery 套件:

<!-- mail.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

接著我們會開始寫 JavaScript。首先我們要在表單送出前做某些事情:

<!-- mail.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$('form').on('sumbit', function(){
    // 送出表單前會觸發這個部分
});
</script>

接著使用 $.ajax 來送出 Ajax 請求:

$('form').on('submit', function(){
    $.ajax({
        url: 'send.php',              // 要傳送的頁面
        method: 'POST',               // 使用 POST 方法傳送請求
        dataType: 'json',             // 回傳資料會是 json 格式
        data: $('form').serialize(),  // 將表單資料用打包起來送出去
        success: function(res){
            // 成功以後會執行這個方法
        },
    });
    return false;  // 阻止瀏覽器跳轉到 send.php,因為已經用 ajax 送出去了
});

讓我們想想成功以後要執行什麼方法。剛剛已經把 PHP 改成回應 {"success": true}{"success": false},所以我們只要檢查 suceess 即可:

success: function(res){
    if(res.success === true){
        alert('傳送成功');
    }else{
        alert('傳送失敗');
    }
},

到這裡就完成基本的 Ajax 請求了。

讓網頁更好?

雖然我們成功不跳離這個頁面,但既然都做了 Ajax,是否可以做得更好?

按下送出按鈕到跳出 alert 這段期間,考量到後端執行程式碼跟送 mail 請求到 smtp 伺服器(可能是 Gmail)時的延遲,至少會有個半秒到三秒的時間吧?使用者是否只能在這裡乾等,甚至有可能重複按了幾次。通常會在這裡加入 loading 動畫,但如果沒有前端基礎,至少可以在這裡把按鈕的文字改成「傳送中」。

jQuery 可以是個高級前端套件,除了做 Ajax 請求外,其實還能改變元素的文字之類的。像剛剛 $('form') 其實就是把表單元素選起來,如果我們要改變 <input type="submit"> 這個元素的文字的話:

$('input[type="submit"]').text('傳送中......');

最後把它放到按下按鈕後,發送請求前的地方:

$('form').on('submit', function(){
    $('input[type="submit"]').text('傳送中......');
    $.ajax({
        url: 'send.php',
        method: 'POST',
        dataType: 'json',
        data: $('form').serialize(),
        success: function(res){
            if(res.success === true){
                alert('傳送成功');
            }else{
                alert('傳送失敗');
            }
        },
    });
    return false;
});

最後整理一下目前的樣子吧:

/* send.php */
require 'PHPMailerAutoload.php';

$email = $_POST['email'];
$name = $_POST['name'];
$subject = $_POST['subject'];
$body = $_POST['body'];

$mail = new PHPMailer;
$mail->setFrom('from@example.com', 'Your Name');
$mail->addAddress($email, $name);
$mail->Subject  = $subject;
$mail->Body     = $body;
if(!$mail->send()) {
    echo '{"success": false}';
} else {
    echo '{"success": true}';
}
<!-- mail.html -->
<form action="send.php" method="post">
    <input type="email" name="email" placeholder="收件人信箱" />
    <input type="text" name="name" placeholder="收件人姓名" />
    <input type="text" name="subject" placeholder="主旨" />
    <textarea name="body" placeholder="內容"></textarea>
    <input type="submit" value="傳送" />
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$('form').on('submit', function(){
    $('input[type="submit"]').text('傳送中......');
    $.ajax({
        url: 'send.php',
        method: 'POST',
        dataType: 'json',
        data: $('form').serialize(),
        success: function(res){
            if(res.success === true){
                alert('傳送成功');
            }else{
                alert('傳送失敗');
            }
        },
    });
    return false;
});
</script>

結語

看到這邊你可能會想,噢天啊怎麼單純一個表單加上 Ajax 可以搞得那麼複雜,別忘了我們還沒有加上動畫、通知,甚至是開始載入寄件備份之類的東西呢。

就是因為要操作的 UI 元素太多,大家才會開始使用 Vue、React 等前端套件吧。