Notion — это многофункциональное приложение для организации любого контента, от заметок до календарей и напоминаний. В нашей последней статье «Начало работы с Notion API и его JavaScript SDK «мы углубились в то, как использовать API Notion и создали небольшой интерфейс для взаимодействия с ним. В этой статье мы рассмотрим еще один пример использования Notion API: создание викторины JavaScript.
Хотя для изучения этой статьи не требуется никаких предварительных знаний (я предоставлю все необходимые шаги), мы будем иметь дело с внешним и внутренним кодом, так как потребуется немного Node.js и
Настройка проекта викторины JavaScript
Мы собираемся разделить нашу установку на две части. В первой мы пройдем необходимую настройку на стороне Notion, а во второй части мы будем работать с нашим кодом.
Чтобы продолжить, вам понадобится учетная запись Notion (подробнее об этом ниже), а также последняя копия Node, установленная на вашем компьютере. Как всегда, окончательный код руководства можно найти на GitHub.
Настройка понятия
Если у вас еще нет учетной записи Notion, создайте ее, перейдя по этой ссылке. После создания учетной записи и входа в систему создайте новую страницу, выбрав Добавить страницу и дав ей имя. В этом руководстве мы будем использовать Tableбазу данных. Хотя это не идеальная база данных для создания викторины, это самое близкое, чего мы можем достичь с помощью Notion!
Внесение информации в таблицу
Теперь, когда у нас есть пустой файл Table, нам нужно выяснить, как правильно вставить в него нашу информацию.
Наша предполагаемая схема для нашей викторины следующая:
{
«1»: {
«Question»: «Which is the purpose of JavaScript?»,
«Answers»: {
«1»: «To style HTML Pages»,
«2»: «To add interactivity to HTML pages»,
«3»: «To perform server side scripting operations»
},
«Correct»: «To add interactivy to HTML pages»
},
«2»: {
«Question»: «To insert a JavaScript into an HTML page, which tag is used?»,
«Answers»: {
«1»: «
|--------- Database ID --------|
Если нас беспокоит наличие ключей API Notion в файле внутри нашего репозитория, обратите внимание, что.gitignoreу нас есть.envфайл; позволяет нам.gitignoreпомещать внутрь разные имена файлов/папок, а это означает, что эти файлы/папки не будут добавлены в наш репозиторий, когда мы отправим наш код.
Теперь, когда у нас есть начальный репозиторий и необходимые учетные данные от Notion, мы можем начать работу над нашей викториной!
Получение данных викторины JavaScript
Сначала мы должны проверить, успешно ли мы подключились к нашей базе данных Notion, поэтому мы перейдем к нашему.index.jsфайлу и зарегистрируем нашу reponseпеременную (посмотрите, как мы получаем нашу databaseIdиз нашего.envфайла и используем ее в нашем запросе к базе данных?).
Если мы затем запустим yarn start, мы должны увидеть
Какой журнал извлекает ответный запрос
Увидев это на нашем терминале, мы правильно подключились к нашей базе данных Notion и теперь можем получить необходимые данные. Наша getDatabaseфункция будет выглядеть так:
exports.getDatabase = async function () {
const response = await notion.databases.query ({ database_id: databaseId }) ;
const responseResults = response.results.map ((page) => {
return {
id: page.id,
question: page.properties.Question.title[0].plain_text,
answers: page.properties.Answers.multi_select,
correct: page.properties.Correct.rich_text[0].plain_text,
};
}) ;
return responseResults;
};
С помощью responseResultsмы сопоставляем наши results (соответствующие записи в нашей базе данных) и сопоставляем пути для различных свойств с именами, которые мы выбираем (в данном случае, id, questionи answers) correct. Обратите внимание, насколько конкретен путь к объекту. Это предусмотрено дизайном, а это означает, что при разработке и работе с собственной базой данных вы должны постоянно исследовать возвращаемые свойства, пока не найдете нужную информацию (на самом деле это вопрос проб и ошибок).
С этим новым кодом мы в значительной степени обращаемся к нашему API и выбираем свойства, которые мы хотим использовать в нашем коде, что означает, что мы готовы работать с ними в нашем интерфейсе!
Отображение наших данных в браузере
Давайте начнем с нашего HTML и CSS, так как они довольно прямолинейны! Мы не будем вносить никаких изменений в наш
.questionContainer {
padding: 30px;
display: flex;
}
.numberElement {
margin: 0px auto 10px;
}
.question {
margin: 0px auto 40px;
}
.answersDiv {
width: 100%;
display: flex;
gap: 20px;
}
.answerOption {
padding: 20px;
margin: 0px;
cursor: pointer;
border: 1px solid rgb (42, 43, 44) ;
}
Мы пока не увидим этих изменений стиля, но теперь мы можем сосредоточиться на функциональности, которая является более важной частью этого руководства.
Если мы теперь перейдем к main.jsфайлу внутри publicпапки, мы увидим, что мы уже получаем наши данные из нашей серверной части с помощью getDataFromBackendфункции. Если вас это смущает, в разделе «Начало работы с Notion API и его JavaScript SDK «есть более подробное объяснение, но в основном server.jsмы создали маршрут, который получает нашу базу данных, и здесь getDataFromBackendмы делаем fetchдля этого тот же маршрут, который заберет наши данные для нас.
Обратите внимание, как внутри addDataу нас уже есть const data = await getDataFromBackend () ;. Это означает, что мы готовы начать работу с нашими данными, и мы действительно можем это проверить! logэту dataпеременную, и мы должны увидеть на нашей консоли массив записей нашей базы данных.
Регистрация наших данных
Отображение наших данных на экране
Теперь, когда мы знаем, как выглядят наши возвращенные данные, нам нужно подумать о том, как мы на самом деле хотим отобразить их на экране. Моя идея состоит в том, чтобы иметь карточку для каждого вопроса с разными ответами внутри, и когда пользователь нажимает правильный ответ, фон ответа должен стать зеленым; если это неправильно, он должен стать красным.
Начнем с создания файла
Итак, внутри нашей addDataфункции мы можем сделать это:
const addData = async () => {
const data = await getDataFromBackend () ;
data.forEach ((value, index) => {
const div = document.createElement ('div’) ;
div.classList.add ('questionContainer’) ;
container.append (div) ;
}) ;
};
Но он все еще кажется немного пустым, поэтому давайте добавим заголовок для каждой карты, например:
const addData = async () => {
const data = await getDataFromBackend () ;
data.forEach ((value, index) => {
const div = document.createElement ('div’) ;
div.classList.add ('questionContainer’) ;
const numberElement = document.createElement ('p’) ;
numberElement.classList.add ('numberElement’) ;
numberElement.innerHTML = `Question ${index + 1}`;
div.appendChild (numberElement) ;
container.append (div) ;
}) ;
};
Здесь мы создаем и присваиваем ему класс, и мы работаем с index + 1, потому что массивы в JavaScript отсчитываются от нуля, и мы не хотим видеть Вопрос 0, так как это не имеет смысла! Если мы сейчас запустим наше приложение, мы должны увидеть
Теперь мы визуализируем карточку для каждого вопроса и отображаем для него заголовок, который гласит «Вопрос ${index + 1}».
Самое интересное: новые функции для рендеринга вопросов и ответов
Теперь самое интересное! Мы могли бы сделать всю нашу логику внутри addData, но это может стать слишком запутанным, поэтому мы собираемся создать новые функции для отображения нашего вопроса и ответов.
Давайте начнем с вопроса, и давайте немного поработаем над нашей addDataфункцией, которая пока не принесет многого:
const addData = async () => {
const data = await getDataFromBackend () ;
data.forEach ((value, index) => {
const div = document.createElement ('div’) ;
div.classList.add ('questionContainer’) ;
const numberElement = document.createElement ('p’) ;
numberElement.classList.add ('numberElement’) ;
numberElement.innerHTML = `Question ${index + 1}`;
div.appendChild (numberElement) ;
// OUR NEWLY ADDED CODE
const question = createQuestion (value.question) ;
div.appendChild (question) ;
// END OF OUR NEWLY ADDED CODE
container.append (div) ;
}) ;
};
Код, который мы только что добавили, очень похож на код для numberElement, но здесь мы присваиваем функцию переменной и добавляем эту переменную. Заметьте также, что мы переходим value.questionв наш createQuestion, потому что мы, конечно же, хотим работать с вопросом и визуализировать его. Все это будет иметь смысл в кратчайшие сроки — не волнуйтесь!
Теперь снаружи и выше addDataдавайте создадим эту новую createQuestionфункцию. Внутри него нам нужна та же логика, которую мы добавили для нашего numberElement: создать элемент, присвоить ему класс и добавить к нему некоторый контент. Здесь мы будем использовать not innerHTMLbut createTextNode: поскольку наши вопросы связаны с кодом, если бы мы использовали что- innerHTMLто вроде text, это фактически отображало бы слово, textно жирным шрифтом вместо всего синтаксиса (вы можете увидеть пример здесь). Наш финал createQuestionбудет выглядеть так:
const createQuestion = (question) => {
const questionElement = document.createElement ('h3') ;
questionElement.classList.add ('question’) ;
const questionNode = document.createTextNode (question) ;
questionElement.appendChild (questionNode) ;
return questionElement;
};
Если мы сейчас запустим yarn start, наш браузер должен выглядеть, как показано ниже.
Теперь мы должны увидеть карточку вопроса с заголовком вопроса и недавно добавленным вопросом.
Теперь наша установка для наших ответов почти такая же. Давайте сначала сделаем то же самое, что мы сделали с createQuestioninside addData:
const addData = async () => {
const data = await getDataFromBackend () ;
data.forEach ((value, index) => {
const div = document.createElement ('div’) ;
div.classList.add ('questionContainer’) ;
const numberElement = document.createElement ('p’) ;
numberElement.classList.add ('numberElement’) ;
numberElement.innerHTML = `Question ${index + 1}`;
div.appendChild (numberElement) ;
const question = createQuestion (value.question) ;
div.appendChild (question) ;
// OUR NEWLY ADDED CODE
const answers = createAnswers (value) ;
div.appendChild (answers) ;
// END OF OUR NEWLY ADDED CODE
container.append (div) ;
}) ;
};
И теперь наш первоначальный поиск createAnswersбудет выглядеть так:
const createAnswers = (value) => {
const answersDiv = document.createElement ('div’) ;
answersDiv.classList.add ('answersDiv’) ;
return answersDiv;
};
Обратите внимание, как мы работаем const answers = createAnswers (value) ;. Мы не можем просто перейти value.answersк нашей функции, потому что нам также нужен файл value.correct. Мы могли бы вместо этого передать нашей функции два аргумента: один для массива ответов, а другой будет правильным.
Отображение массива ответов
Теперь у нас есть массив ответов, и нам нужно отобразить их все, поэтому нам нужен цикл, чтобы просмотреть их все. Процесс внутри этого цикла будет в значительной степени таким же, как и для всех других элементов, поэтому на этом этапе мы должны быть профессионалами в рендеринге элементов в DOM:
const createAnswers = (value) => {
const answersDiv = document.createElement ('div’) ;
answersDiv.classList.add ('answersDiv’) ;
for (let i = 0; i < value.answers.length; i++) {
const answerElement = document.createElement ('p’) ;
answerElement.classList.add ('answerOption’) ;
const answerNode = document.createTextNode (value.answers[i].name) ;
answerElement.appendChild (answerNode) ;
answersDiv.appendChild (answerElement) ;
}
return answersDiv;
};
С помощью этого кода мы зацикливаемся на нашем array, создаем элемент, присваиваем ему класс и используем createTextNodeего для отображения наших ответов. (Как ни странно, если бы мы использовали innerHTMLздесь, ответы с
Частичный вид нашей викторины со всеми необходимыми элементами
Взаимодействие с ответами
Хм... Но мы не можем взаимодействовать с ответами, и это не совсем викторина, если мы не узнаем, правильно мы ответили или нет, верно? Мы должны это исправить!
Мы знаем, что хотим щелкнуть по каждому ответу и узнать, правильный он или неправильный, поэтому мы можем начать с добавления к нему прослушивателя событий следующим образом:
const createAnswers = (value) => {
const answersDiv = document.createElement ('div’) ;
answersDiv.classList.add ('answersDiv’) ;
for (let i = 0; i < value.answers.length; i++) {
const answerElement = document.createElement ('p’) ;
answerElement.classList.add ('answerOption’) ;
const answerNode = document.createTextNode (value.answers[i].name) ;
// OUR NEWLY ADDED CODE
answerElement.addEventListener ('click’, () => {}) ;
// END OF OUR NEWLY ADDED CODE
answerElement.appendChild (answerNode) ;
answersDiv.appendChild (answerElement) ;
}
return answersDiv;
};
Помните, что мы сделали createAnswers (value), чтобы мы могли получить value.correct? Теперь пришло время сиять! Когда мы нажимаем на ответ, возможны два результата: пользователь выбирает ответ, равный правильному ответу, или пользователь выбирает ответ, не равный правильному ответу. Чтобы справиться с этими возможными результатами, мы собираемся использовать оператор if, и способ, которым мы собираемся показать нашим пользователям, что они получили правильный или неправильный ответ, — это изменение
const createAnswers = (value) => {
const answersDiv = document.createElement ('div’) ;
answersDiv.classList.add ('answersDiv’) ;
for (let i = 0; i < value.answers.length; i++) {
const answerElement = document.createElement ('p’) ;
answerElement.classList.add ('answerOption’) ;
const answerNode = document.createTextNode (value.answers[i].name) ;
answerElement.addEventListener ('click’, () => {
// OUR NEWLY ADDED CODE
answerElement.style.color = 'white’;
if (value.answers[i].name≠= value.correct) {
// colour our answerElement red
answerElement.style.backgroundColor = '#f55142';
} else {
// colour our answerElement green
answerElement.style.backgroundColor = '#6dbf39';
}
// END OF OUR NEWLY ADDED CODE
}) ;
answerElement.appendChild (answerNode) ;
answersDiv.appendChild (answerElement) ;
}
return answersDiv;
};
Таким образом, с каждым щелчком мы меняем цвет текста на белый, а затем проверяем, nameравно ли свойство каждого ответа value.correct (это, очевидно, не идеально, и индекс был бы намного лучше, но мы сделали лучше всего с базами данных Notion!). Если это не так, мы меняем его цвет на красный, а если да, то меняем на зеленый!
Наша викторина теперь завершена с полным взаимодействием для правильных и неправильных ответов.
Вот и закончилась наша викторина! ? Разве это не фантастика?
Завершение викторины Notion JavaScript
В этом уроке мы рассмотрели множество функций, предоставляемых API Notion, и, честно говоря, всегда очень интересно видеть, как много вы можете сделать с помощью такого простого инструмента!
Я надеюсь, что этот пост вдохновит вас на изучение Notion API и создание собственных викторин и других интересных вещей с помощью Notion!
Если вы хотите быстро протестировать этот проект Notion, вы можете клонировать его из нашего репозитория GitHub.