Послушайте, мы все знаем, что Google Forms действительно удобны и полезны, но их дизайн и ограниченные возможности настройки оставляют желать лучшего. Если вы используете Google Forms для бизнеса, вы можете предпочесть Forms из-за их функциональности, но из-за жесткого стиля и ограниченной настройки они могут показаться непрофессиональными.

В этом посте я покажу вам, как:

Создайте скрипт Google, который отправляет ответы на вашу электронную почту

Создайте HTML

Бонус —Отображаемые ответы (необязательно)

Шаг 1. Создайте скрипт Google, который отправляет ответы на ваши электронные письма

Создайте новый Google Sheet, назовите его как хотите. Однако первым столбцом вашего листа должна быть «Временная метка».

Далее переходим в Tools → Script Editor (отдельная благодарность Martin Hawksey)

var TO_ADDRESS = "YOUR_EMAIL";

function formatMailBody(obj, order) {
  var result = "";
  if (!order) {
    order = Object.keys(obj);
  }
  
  // loop over all keys in the ordered form data
  for (var idx in order) {
    var key = order[idx];
    result += "<h4 style='text-transform: capitalize; margin-bottom: 0'>" + key + "</h4><div>" + sanitizeInput(obj[key]) + "</div>";
    // for every key, concatenate an `<h4 />`/`<div />` pairing of the key name and its value, 
    // and append it to the `result` string created at the start.
  }
  return result; // once the looping is done, `result` will be one long string to put in the email body
}
// sanitize content from the user - trust no one 
// ref: https://developers.google.com/apps-script/reference/html/html-output#appendUntrusted(String)
function sanitizeInput(rawInput) {
   var placeholder = HtmlService.createHtmlOutput(" ");
   placeholder.appendUntrusted(rawInput);
  
   return placeholder.getContent();
 }
function doPost(e) {
try {
    Logger.log(e); // the Google Script version of console.log see: Class Logger
    record_data(e);
    
    // shorter name for form data
    var mailData = e.parameters;
// names and order of form elements (if set)
    var orderParameter = e.parameters.formDataNameOrder;
    var dataOrder;
    if (orderParameter) {
      dataOrder = JSON.parse(orderParameter);
    }
    
    // determine recipient of the email
    // if you have your email uncommented above, it uses that `TO_ADDRESS`
    // otherwise, it defaults to the email provided by the form's data attribute
    var sendEmailTo = (typeof TO_ADDRESS !== "undefined") ? TO_ADDRESS : mailData.formGoogleSendEmail;
    
    // send email if to address is set
    if (sendEmailTo) {
      MailApp.sendEmail({
        to: String(sendEmailTo),
        subject: "YOUR_SUBJECT", //come up with a subject line
        // replyTo: String(mailData.email), // This is optional and reliant on your form actually collecting a field named `email`
        htmlBody: formatMailBody(mailData, dataOrder)
      });
    }
return ContentService    // return json success results
          .createTextOutput(
            JSON.stringify({"result":"success",
                            "data": JSON.stringify(e.parameters) }))
          .setMimeType(ContentService.MimeType.JSON);
  } catch(error) { // if error return this
    Logger.log(error);
    return ContentService
          .createTextOutput(JSON.stringify({"result":"error", "error": error}))
          .setMimeType(ContentService.MimeType.JSON);
  }
}
/**
 * record_data inserts the data received from the html form submission
 * e is the data received from the POST
 */
function record_data(e) {
  var lock = LockService.getDocumentLock();
  lock.waitLock(30000); // hold off up to 30 sec to avoid concurrent writing
  
  try {
    Logger.log(JSON.stringify(e)); // log the POST data in case we need to debug it
    
    // select the 'responses' sheet by default
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    var sheetName = e.parameters.formGoogleSheetName || "responses";
    var sheet = doc.getSheetByName(sheetName);
    
    var oldHeader = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    var newHeader = oldHeader.slice();
    var fieldsFromForm = getDataColumns(e.parameters);
    var row = [new Date()]; // first element in the row should always be a timestamp
    
    // loop through the header columns
    for (var i = 1; i < oldHeader.length; i++) { // start at 1 to avoid Timestamp column
      var field = oldHeader[i];
      var output = getFieldFromData(field, e.parameters);
      row.push(output);
      
      // mark as stored by removing from form fields
      var formIndex = fieldsFromForm.indexOf(field);
      if (formIndex > -1) {
        fieldsFromForm.splice(formIndex, 1);
      }
    }
    
    // set any new fields in our form
    for (var i = 0; i < fieldsFromForm.length; i++) {
      var field = fieldsFromForm[i];
      var output = getFieldFromData(field, e.parameters);
      row.push(output);
      newHeader.push(field);
    }
    
    // more efficient to set values as [][] array than individually
    var nextRow = sheet.getLastRow() + 1; // get next row
    sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// update header row with any new data
    if (newHeader.length > oldHeader.length) {
      sheet.getRange(1, 1, 1, newHeader.length).setValues([newHeader]);
    }
  }
  catch(error) {
    Logger.log(error);
  }
  finally {
    lock.releaseLock();
    return;
  }
}
function getDataColumns(data) {
  return Object.keys(data).filter(function(column) {
    return !(column === 'formDataNameOrder' || column === 'formGoogleSheetName' || column === 'formGoogleSendEmail' || column === 'honeypot');
  });
}
function getFieldFromData(field, data) {
  var values = data[field] || '';
  var output = values.join ? values.join(', ') : values;
  return output;
}
function onFormSubmit (e) {
  var doc = SpreadsheetApp.getActiveSpreadsheet();
  var sheetName = e.parameters.formGoogleSheetName || "responses";
  var sheet = doc.getSheetByName(sheetName);
  var data = sheet.getDataRange().getValues();
  var userNames = sheet.getDataRange().offset(1, 1, sheet.getLastRow()-1,1).getValues().join();
  
  
  var re = new RegExp(event.namedValues["name"],"g");
  var count = userNames.match(re).length;
  
  if (count != 1 ) {
    
    sheet.deleteRow(event.range.getRow());
  }
  
  
}

Этот сценарий выполняет все, что нам нужно, а именно: каждый раз, когда кто-то отвечает на форму, он обновляет электронную таблицу и отправляет уведомление на вашу электронную почту. Сохраните программу и выполните следующие действия для ее развертывания:

Перейдите в «Опубликовать» → «Развернуть как веб-приложение».

Ваш адрес электронной почты используется по умолчанию для «Выполнить приложение как:», а для «Кто имеет доступ к приложению:» вы должны выбрать «Любой, даже анонимный». Нажмите «Обновить» и скопируйте URL-адрес скрипта.

Создайте HTML

Сохраните приведенное ниже как index.html.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="contact form example">
<link href='https://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet'>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link rel="stylesheet" href="style.css">
<title>MY FORM</title>
<style>
</style>
</head>
<body>
<h2>Math Quiz</h2>
<form class="gform pure-form pure-form-stacked" method="POST" data-email="[email protected]"
action="YOUR_SCRIPT_URL">
<!-- change the form action to your script url -->
<div class="form-elements">
<fieldset class="pure-group">
<label for="name">Username</label>
<input id="name" name="name" placeholder="username" />
</fieldset>
<fieldset class="pure-group">
<label for="answer">What's 2 + 2?</label>
<input id="answer" name="answer" placeholder="Answer" />
</fieldset>
<br>
<button class="button-success pure-button button-xlarge">Submit!</button>
</div>
<!-- Customize the Thankyou Message People See when they submit the form: -->
<br><br>
<div class="thankyou_message" style="display:none;">
<h2><em><b>Thanks for your response!</b></em>
</h2>
</div>
</form>
<!-- Submit the Form to Google Using "AJAX" -->
<script data-cfasync="false" src="form-submission-handler.js"></script>
<!-- END -->
</body>
</html>

Сохраните приведенное ниже как form-submission-handler.js.

(function() {
// get all data in form and return object
function getFormData(form) {
var elements = form.elements;
var honeypot;
var fields = Object.keys(elements).filter(function(k) {
if (elements[k].name === "honeypot") {
honeypot = elements[k].value;
return false;
}
return true;
}).map(function(k) {
if(elements[k].name !== undefined) {
return elements[k].name;
// special case for Edge's html collection
}else if(elements[k].length > 0){
return elements[k].item(0).name;
}
}).filter(function(item, pos, self) {
return self.indexOf(item) == pos && item;
});
var formData = {};
fields.forEach(function(name){
var element = elements[name];
// singular form elements just have one value
formData[name] = element.value;
// when our element has multiple items, get their values
if (element.length) {
var data = [];
for (var i = 0; i < element.length; i++) {
var item = element.item(i);
if (item.checked || item.selected) {
data.push(item.value);
}
}
formData[name] = data.join(', ');
}
});
// add form-specific values into the data
formData.formDataNameOrder = JSON.stringify(fields);
formData.formGoogleSheetName = form.dataset.sheet || "responses"; // default sheet name
formData.formGoogleSendEmail
= form.dataset.email || ""; // no email by default
return {data: formData, honeypot: honeypot};
}
function handleFormSubmit(event) {  // handles form submit without any jquery
event.preventDefault();           // we are submitting via xhr below
var form = event.target;
var formData = getFormData(form);
var data = formData.data;
// If a honeypot field is filled, assume it was done so by a spam bot.
if (formData.honeypot) {
return false;
}
disableAllButtons(form);
var url = form.action;
var xhr = new XMLHttpRequest();
xhr.open('POST', url);
// xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
form.reset();
var formElements = form.querySelector(".form-elements")
if (formElements) {
formElements.style.display = "none"; // hide form
}
var thankYouMessage = form.querySelector(".thankyou_message");
if (thankYouMessage) {
thankYouMessage.style.display = "block";
}
}
};
// url encode form data for sending as post data
var encoded = Object.keys(data).map(function(k) {
return encodeURIComponent(k) + "=" + encodeURIComponent(data[k]);
}).join('&');
xhr.send(encoded);
}
function loaded() {
// bind to the submit event of our form
var forms = document.querySelectorAll("form.gform");
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener("submit", handleFormSubmit, false);
}
};
document.addEventListener("DOMContentLoaded", loaded, false);
function disableAllButtons(form) {
var buttons = form.querySelectorAll("button");
for (var i = 0; i < buttons.length; i++) {
buttons[i].disabled = true;
}
}
})();

Примечание.В каждом наборе полей «name=» должно совпадать с названием столбца Google Sheet.

Вот и все! Вы можете оформить страницу так, как вам нравится. Форма теперь полностью настраиваемая, и каждый ответ отправляется на вашу электронную почту.

Бонус — отображение результатов (необязательно)

Если форма похожа на опрос и вы хотите показать участнику результаты сразу после отправки, вернитесь в Google Sheet. Выберите «Файл» → «Опубликовать в Интернете» → «Выберите, чтобы опубликовать только диаграмму». Скопируйте ссылку.

В index.html вы можете добавить эти строки кода под сообщением с благодарностью:

<div id="wrapper">
<iframe width="600" height="371" seamless frameborder="0" scrolling="no" src="INSERT_CHART_LINK_HERE"></iframe>
</div>

Теперь всякий раз, когда человек отправляет ответ, он может видеть результаты в реальном времени на той же странице!