Первая официальная викторина Nettuts + имела огромный успех, внушительное количество разработчиков приняло участие и оценило их знания. Было нетривиальное количество комментариев, спрашивающих, как был построен механизм мини-викторины. И о чудо! Вот чему мы научимся сегодня.
Слово от автора
Тесты — отличный способ привлечь сообщество — просто взгляните на наш недавний тест. Проблема в том, что в наше время люди ожидают, что тесты будут довольно интерактивными, и мое Google-фу сильно провалилось при поиске системы, которая облегчает процесс. Именно тогда я решил создать себя, и Nettuts, супер простой тестовый движок, который я игриво называю kroggy.
Я верю, что демонстрация стоит тысячи слов. Попробуйте демо и попробуйте сами.
Сегодня мы рассмотрим, как это реализовать, как вы уже догадались, с нашей любимой библиотекой JavaScript, jQuery. Заинтересованы? Давайте начнем прямо сейчас!
Цели дизайна
Цель дизайна для этой версии невероятно проста. Я провожу вас через те моменты, которые я имел в виду.
- Мы получим что-то, что выглядит гладко и элегантно — то, что предлагает пользователю пройти тест.
- Это не место для длинного списка вопросов с переключателями и ярлыками. Нам нужно будет внедрить простой слайдер для просмотра каждого вопроса в отдельности.
- Вопросы будут оцениваться в конце викторины, а не сразу. Таким образом, читатель может сосредоточиться на процессе принятия теста вместо того, чтобы каждый раз останавливаться, чтобы оценить результат.
- Простой индикатор в нижней части, чтобы отобразить прогресс пользователя.
- Правильные ответы могут жить либо в JavaScript, либо в HTML. Это не SAT — нет необходимости вносить больше сложности в нашу начинающую кодовую базу.
Некоторые заметные особенности, из которых я отказываюсь:
- Нет пост викторины отзывы. Это привело бы к тому, что мы очистили бы наш собственный HTML-код для данных, а затем повторно обработали его. Не смешно! И определенно не в рамках сегодняшнего урока.
- Мы не собираемся превращать это в плагин. Нам лучше сосредоточиться на фактической части разработки функциональности. Существует множество материалов о том, как преобразовать код в плагин jQuery.
- Как расширение вышеупомянутого пункта, это также означает, что HTML для тестов будет предварительно написан нами — он не будет генерироваться и вставляться динамически.
Вот и все, наверное. Давайте перейдем к тому, как мы собираемся сделать это.
План действий
Теперь нам нужно наметить, что нужно сделать в определенном порядке.
- Шаг 1. Создайте HTML-разметку для базового движка викторины и соответствующим образом стилизуйте
- Шаг 2: Когда пользователь выбрал опцию и нажал следующую кнопку, просто молча перейдите к следующему вопросу. Если опция не выбрана, выведите ошибку.
- Шаг 3: Когда пользователь находится на последнем вопросе и нажимает на следующую кнопку, поднимите ад и оцените результаты следующим образом.
- Шаг 4: Узнайте, какой вариант выбрал пользователь, и сопоставьте его со списком предварительно определенных ответов. Верните простой массив, чтобы мы могли оценить результаты позже и представить некоторые данные на последнем экране.
Хорошо, это звучит довольно просто. Это основные шаги в создании этой функциональности. Конечно, есть несколько других мелких вещей, но я объясню их по ходу дела.
Теперь давайте углубимся в некоторый код и запачкаем руки. Во-первых, HTML.
Основная разметка
Разметка является одним из столпов, поддерживающих этот движок, поэтому мы рассмотрим каждую часть отдельно, а весь код будет показан в конце, чтобы показать общую картину.
1
2
|
<div id=»main-quiz-holder»>
</div>
|
Элемент div с идентификатором main-quiz-holder
будет основным контейнером, внутри которого мы собираемся поместить весь наш HTML-код — весь приведенный ниже код находится внутри этого элемента div.
1
2
|
<div class=»questionContainer»>
</div>
|
Далее, давайте создадим образец слайда теста. Помните, что мы собираемся создать супер простой слайдер для хранения наших индивидуальных вопросов. Каждый слайд будет содержать элемент div с классом questionContainer
.
Прежде чем перейти к разметке слайда викторины, давайте разберемся с некоторыми формальностями.
1
2
3
|
<div id=»intro-container» class=»questionContainer»>
<a class=»btnStart» href=»#»><img src=»img/start.png» /></a>
</div>
|
Приведенный выше код предназначен для начального контейнера, который отображается с просьбой пройти тест. Мы просто добавляем в него идентификатор intro-container
чтобы позже мы могли лучше его оформить.
Внутри div у нас есть только одно изображение-заставка, обернутое элементом привязки.
1
2
3
|
<div id=»results-container» class=»questionContainer»>
<div id=»resultKeeper»></div>
</div>
|
И результаты скользят. Здесь нет ничего особенного: мы присваиваем специальный идентификатор и помещаем в него пустой div для хранения наших результатов. Мы заполним этот элемент в будущем, когда будем оценивать результаты.
Если вы заметили класс questionContainer
для этого и слайда выше, золотая звезда для вас! Мы добавляем этот класс, так как оба этих контейнера являются частью слайдера.
1
2
|
<div id=»progressKeeper» ><div id=»progress»></div></div>
<div id=»notice»>Please select an option</div>
|
И, наконец, небольшая уборка. Мы создаем элементы div для контейнера индикатора выполнения, сам фактический индикатор и уведомление, которое будет отображаться, если пользователь не выберет опцию.
На этом наша разметка скелета закончилась.
Разметка для каждого вопроса
Ааа, мясистая часть разметки на нас. Эти разделы могут показаться сложными, но, как всегда, я разбью их на более мелкие куски и проведу вас по коду.
1
2
3
4
5
6
7
8
|
<div class=»questionContainer»>
<div class=»question»>Question</div>
<!— UL with options —>
<div class=»btnContainer»>
<!— Internal navigation links —>
</div>
</div>
</div>
|
Как упоминалось ранее, мы будем заключать каждый слайд нашего теста в элемент div с классом questionContainer
. Внутри у нас есть элемент div с классом question
который содержит каждый из наших вопросов.
Далее мы приводим неупорядоченный список, содержащий возможные ответы на вопрос, и div, выполняющий роль контейнера для элементов навигации [предыдущий / следующий]. Запишите имя класса каждого из этих разделов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<ul class=»answers»>
<li>
<label><input data-key=»a» type=»radio»>lend structure to the document</label>
</li>
<li>
<label><input data-key=»b» type=»radio»>mold the presentation of the document</label>
</li>
<li>
<label><input data-key=»c» type=»radio»>script the interactions on the page</label>
</li>
<li>
<label><input data-key=»d» type=»radio»>You’re crafty!
</li>
</ul>
|
Расширяясь в нашем неупорядоченном списке, каждый элемент li
имеет элемент label, содержащий одну радиокнопку. Заметьте, что мы назначаем каждому радио атрибут data-key
? Мы посмотрим на это чуть позже.
1
2
3
4
5
|
<div class=»btnContainer»>
<div class=»prev»><a class=»btnPrev» href=»#»>Prev</a></div>
<div class=»next»><a class=»btnNext» href=»#»>Next</a></div>
<div class=»clear»></div>
</div>
|
И, наконец, навигационный контейнер. Ничего особенного здесь. У нас есть основной div с классом btnContainer
который действует как контейнер. Внутри у нас есть якорь, обернутый элементом div. DIV просто для стилизации, так что не стесняйтесь отказаться от этого в вашей реализации.
Имейте в виду, что предыдущая и следующая кнопки должны быть вставлены логически. Вы не хотели бы, чтобы предыдущая кнопка на первом фактическом слайде опроса, в то время как на последнем слайде должна быть кнопка для запуска процесса оценки.
Вместо следующей кнопки последний слайд должен иметь следующее:
1
|
<div class=»next»><a class=»btnShowResult» href=»#»>Finish</a></div>
|
И это завершает HTML-часть наших слайдов викторины. Вы заметите, что мы не взялись за HTML для результатов теста. Мы займемся этим на этапе JavaScript. А пока давайте перейдем к презентации.
Конечный HTML
001
002
003
004
005
006
007
008
009
010
011
012
013
014
+015
016
+017
018
019
020
021
022
023
024
025
026
027
028
029
+030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
+055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
|
<div id=»main-quiz-holder»>
<div id=»intro-container» class=»questionContainer»>
<a class=»btnStart» href=»#»><img src=»img/start.png»/></a>
</div>
<div class=»questionContainer hide»>
<div class=»question»>
CSS is used to…
</div>
<ul class=»answers»>
<li>
<label><input data-key=»a» type=»radio»>lend structure to the document</label>
</li>
<li>
<label><input data-key=»b» type=»radio»>mold the presentation of the document</label>
</li>
<li>
<label><input data-key=»c» type=»radio»>script the interactions on the page</label>
</li>
<li>
<label><input data-key=»d» type=»radio»>You’re crafty!
</li>
</ul>
<div class=»btnContainer»>
<div class=»next»>
<a class=»btnNext» href=»#»>Next</a>
</div>
<div class=»clear»>
</div>
</div>
</div>
<div class=»questionContainer hide»>
<div class=»question»>
The C in CSS stands for?
</div>
<ul class=»answers»>
<li>
<label><input data-key=»a» type=»radio»>Crysis</label>
</li>
<li>
<label><input data-key=»b» type=»radio»>Crocodile</label>
</li>
<li>
<label><input data-key=»c» type=»radio»>Consistent</label>
</li>
<li>
<label><input data-key=»d» type=»radio»>Cascading</label>
</li>
</ul>
<div class=»btnContainer»>
<div class=»prev»>
<a class=»btnPrev» href=»#»>Prev</a>
</div>
<div class=»next»>
<a class=»btnNext» href=»#»>Next</a>
</div>
<div class=»clear»>
</div>
</div>
</div>
<!— More questions here —>
<div class=»questionContainer hide»>
<div class=»question»>
The * selector selects…
</div>
<ul class=»answers»>
<li>
<label><input data-key=»a» type=»radio»>every div element</label>
</li>
<li>
<label><input data-key=»b» type=»radio»>only paragraphs</label>
</li>
<li>
<label><input data-key=»c» type=»radio»>only parent elements</label>
</li>
<li>
<label><input data-key=»d» type=»radio»>every element</label>
</li>
</ul>
<div class=»btnContainer»>
<div class=»prev»>
<a class=»btnPrev» href=»#»>Prev</a>
</div>
<div class=»next»>
<a class=»btnShowResult» href=»#»>Finish</a>
</div>
<div class=»clear»>
</div>
</div>
</div>
<div id=»results-container» class=»questionContainer hide»>
<div id=»resultKeeper»>
</div>
</div>
<div id=»progressKeeper»>
<div id=»progress»>
</div>
</div>
<div id=»notice»>
Please select an option
</div>
</div>
|
В конце этого этапа наша страница выглядит так:
Основной CSS
Давайте начнем делать нашу викторину выглядеть гладкой и привлекательной. Во-первых, основной контейнер.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
#main-quiz-holder {
margin: 0 auto;
position: relative;
background: #FCFCFC;
border:1px solid #dedede;
box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-o-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-webkit-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-moz-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
border-radius: 2px;
position: relative;
width: 600px;
font-family: «Myriad», «Myriad Pro», «Helvetica»,»Segoe UI», «Lucida Sans Unicode», «Lucida Grande», sans-serif;
}
|
По сути, мы центрируем его, давая ему фон и границу, чтобы выделить его. Свойства CSS3 могут показаться немного запутанными, но это в основном из-за специфических свойств браузера. Здесь мы просто определяем тень блока для контейнера — ничего сложного, когда вы изучите синтаксис.
1
2
3
4
|
#results-container, #intro-container {
width: 500px;
text-align: center;
}
|
Давайте определим ширину и отцентрируем содержимое вступительного и заключительного слайдов. Таким образом, нам не нужно стилизовать каждый из них по отдельности, так как оба их содержания должны быть в любом случае центрированы.
1
2
3
4
5
6
7
8
9
|
.questionContainer .question, h2.qTitle {
margin: 10px 0 20px 0;
font-size: 26px;
font-weight: normal;
}
h2.qTitle {
font-size: 32px;
margin-top: 30px;
}
|
Давайте стилизовать наши заголовки и вопросы сейчас. Поскольку мы хотим, чтобы они выделялись среди других элементов, мы увеличиваем размеры шрифта и даем им значительные поля с обеих сторон. Помните, куча пустого пространства помогает лучше структурировать содержимое.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
#progressKeeper {
width: 553px;
margin: 0px 12px;
box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-o-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-webkit-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-moz-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
border-radius: 2px;
border:1px solid #dedede;
position: absolute;
bottom: 10px;
left: 10px;
}
|
Теперь к контейнеру индикатора выполнения. Первое, что вам нужно отметить, это position: absolute
объявление. Мы добавляем это, так как нам нужно, чтобы этот элемент оставался фиксированным в родительском контейнере
Свойства bottom and left
указывают, что он должен быть зафиксирован в нижней части контейнера. Остальная часть CSS предназначена только для стилизации.
01
02
03
04
05
06
07
08
09
10
|
#progress {
width: 0;
height: 20px;
color: #4c4c4c;
background: #f6f6f6;
background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#d4d4d4));
background: -webkit-linear-gradient(#f6f6f6, #d4d4d4);
background-image: -moz-linear-gradient(top, #f6f6f6, #d4d4d4);
background-image: -moz-gradient(top, #f6f6f6, #d4d4d4);
}
|
А теперь актуальный индикатор выполнения. Мы хотим, чтобы оно было заниженным и в то же время достаточно заметным, поэтому я использую небольшой серый градиент. Как упоминалось ранее, это может показаться сложным, но это из-за относительно сложного синтаксиса свойства градиента CSS3.
1
2
3
4
5
|
#notice {
position: absolute;
bottom: 40px;
right: 20px;
}
|
notice
является элемент div, который содержит уведомление об ошибке, когда пользователь испортит, скажем, когда он не выбрал опцию. Чтобы упростить ситуацию, я решил расположить ее абсолютно над индикатором выполнения.
Вопрос слайд CSS
1
2
3
4
5
6
7
|
.questionContainer {
width: 560px;
min-height: 400px;
padding: 20px;
overflow: auto;
margin: auto;
}
|
На контейнере слайдов сначала укажите QuestionContainer. Мы определяем фиксированную ширину, чтобы правильно ее центрировать. Мы также определяем минимальную ширину, чтобы она не нарушалась при меньшем количестве вариантов. Наконец, мы добавляем немного отступов для улучшения представления и добавляем объявление overflow: auto
для работы с плавающими дочерними элементами.
1
2
3
4
5
|
.questionContainer ul.answers {
margin: 0px;
padding: 5px;
list-style: none;
}
|
Давайте разберемся со списком ответов дальше. Мы просто удаляем стилизацию для списка, применяя list-style: none
и немного отступов и пробелов для лучшей презентации.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
.questionContainer ul.answers li {
padding: 5px 50px;
margin: 12px 0;
color: #4c4c4c;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
-webkit-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
-moz-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
background: #f6f6f6;
background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#d4d4d4));
background: -webkit-linear-gradient(#f6f6f6, #d4d4d4);
background-image: -moz-linear-gradient(top, #f6f6f6, #d4d4d4);
background-image: -moz-gradient(top, #f6f6f6, #d4d4d4);
border: 1px solid #a1a1a1;
}
|
Выше приведен стиль для отдельных элементов списка. CSS можно разбить на 3 раздела.
- Первые несколько строк очень простые — мы добавляем немного поля и отступов, указываем цвет для текста внутри и добавляем немного радиуса рамки.
- Далее мы используем свойство тени блока CSS3, чтобы оно выглядело лучше. Обратите внимание на разные префиксы для разных браузеров.
- Наконец, мы обрабатываем фон элемента. Мы указываем градиент серого, который будет отображаться.
1
2
3
4
5
6
7
|
.questionContainer ul.answers li.selected {
background: #6fb2e5;
box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
}
|
На стили для элемента li, когда он был выбран пользователем. Нам нужно, чтобы он действительно выделялся, чтобы помочь пользователю. Имея это в виду, я выбираю яркий синий фон для применения.
CSS для элементов навигации
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
.questionContainer .prev, .questionContainer .next {
height: 19px;
cursor: pointer;
padding: 5px 10px;
font-size: 16px;
padding: 5px 10px;
color: #4c4c4c;
border-radius: 4px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
background: #6fb2e5;
box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
}
|
Давайте продолжим и стилизуем элементы div, содержащие элементы навигации. Большая часть кода выше должна быть достаточно объяснительной. Мы определяем некоторые отступы для лучшего обрамления текста внутри, назначая ему цвет, немного увеличивая размер шрифта и добавляя несколько углов округления. И, наконец, мы добавили несколько теней CSS3-блоков, чтобы они выглядели элегантно.
1
2
3
4
5
6
7
|
.questionContainer .next {
background: #77d125;
box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
-o-box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
-webkit-box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
-moz-box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
}
|
Так как мы хотим, чтобы следующая кнопка выглядела иначе, мы переопределяем некоторые из стилей сверху. Здесь я перемещаю основной оттенок с синего на зеленый.
1
2
3
|
.questionContainer .prev { float: left;}
.questionContainer .next, .questionContainer.btnShowResult { float: right;
.questionContainer .clear { clear: both;
|
Это позаботится о положении каждой из кнопок. Мы хотим, чтобы предыдущая кнопка была смещена влево, а следующая и последняя кнопки должны быть смещены вправо. Это именно то, что делает приведенный выше код.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
.btnPrev {
padding-left: 24px;
background: url(img/back.png) left no-repeat;
}
.btnNext {
padding-right: 24px;
background: url(img/forward.png) right no-repeat;
}
.btnShowResult{
padding-left: 24px;
background: url(img/confirm.png) left no-repeat;
}
.btnStart {
display: block;
margin: 40px auto 0 auto;
}
.btnContainer {
margin: 20px 0 30px 0;
padding: 5px;
}
|
На этапе HTML вы должны были заметить, что у самих кнопок есть родительский элемент div с дочерним элементом link. Мы стилизовали родительские элементы в предыдущем блоке кода. Выше обрабатывает дочерние ссылки.
По сути, приведенный выше код вставляет маленький графический элемент в кнопки. Мы добавляем небольшой отступ, чтобы текст был хорошо смещен. Обратите внимание, как мы изменяем между padding-left and padding-right
чтобы точно стилизовать каждый элемент.
Сам btnContainer
получает небольшое поле и отступы, чтобы расположить его там, где мы хотим.
Стилизация результатов
Уф! Большая часть работы CSS теперь позади. Давайте рассмотрим последний фрагмент CSS — последний слайд, который отображает результаты. Поскольку вы еще не видели HTML, это может немного сбивать с толку, но CSS-представление будет довольно общим и легко анализируемым.
1
2
3
4
5
|
.resultRow {
width: 110px;
margin: 10px 25px;
float: left;
}
|
Давайте начнем с малого. Мы собираемся присвоить каждой «ячейке» результатов класс resultRow
. Поскольку мы хотим, чтобы он был представлен аккуратно, мы собираемся переместить все влево, чтобы он сформировал аккуратный стек из трех столбцов результатов. Этот раздел должен выглядеть так после того, как мы закончили:
01
02
03
04
05
06
07
08
09
10
11
12
|
.correct, .wrong {
height: 19px;
cursor: pointer;
font-size: 16px;
padding: 5px 15px;
color: #4c4c4c;
border-radius: 4px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
-webkit-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
-moz-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
}
|
Давайте определим базу этих блоков. Как всегда, я использую немного CSS3, чтобы выделить разделы. Кроме того, CSS довольно прост — немного отступов, закругленных углов и тому подобное.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
.correct {
background: #b2d840;
background: -webkit-gradient(linear, left top, left bottom, from(#b2d840), to(#90b61e));
background: -webkit-linear-gradient(#b2d840, #90b61e);
background-image: -moz-linear-gradient(top, #b2d840, #90b61e);
background-image: -moz-gradient(top, #b2d840, #90b61e);
border: 1px solid #5d8300;
}
.wrong {
background: #e84545;
background: -webkit-gradient(linear, left top, left bottom, from(#e84545), to(#c62323));
background: -webkit-linear-gradient(#e84545, #c62323);
background-image: -moz-linear-gradient(top, #e84545, #c62323);
background-image: -moz-gradient(top, #e84545, #c62323);
border: 1px solid #930000;
color: #F1F1F1;
}
|
Нам нужно, чтобы правильные и неправильные ответы выделялись визуально. Используя CSS3, мы применяем зеленый градиент с оттенком ко всем правильным ответам, в то время как неправильные — не получают красную обработку. Ничего особенного здесь не происходит. Освойте синтаксис, и у вас все будет готово.
1
2
3
4
5
6
7
8
|
.correct span {
padding: 0 20px;
background: url(img/confirm.png) left no-repeat;
}
.wrong span {
padding: 0 20px;
background: url(img/delete.png) left no-repeat;
}
|
Давайте добавим немного изображений, чтобы все это выглядело более сплоченным. Мы добавляем несколько простых иконок внутри кнопок, как показано выше.
1
2
3
4
5
6
7
8
|
#answer-key {
text-align: center;
width: 300px;
padding: 15px;
margin: 0 auto;
clear: both;
font-size: 16px;
}
|
И, наконец, давайте стилизовать контейнер ключа ответа. Немного набивки и поля помогают нам сделать его лучше. Мы также следим за тем, чтобы он был отцентрирован, и устанавливаем размер шрифта на соответствующее значение.
Финальный CSS
Я пропустил несколько общих частей CSS по пути, поэтому вот полный CSS:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
+015
016
+017
018
019
020
021
022
023
024
025
026
027
028
029
+030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
+055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
#main-quiz-holder {
margin: 0 auto;
position: relative;background: #FCFCFC;
border:1px solid #dedede;
box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-o-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-webkit-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-moz-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
border-radius: 2px;
position: relative;
width: 600px;
font-family: «Myriad», «Myriad Pro», «Helvetica»,»Segoe UI», «Lucida Sans Unicode», «Lucida Grande», sans-serif;
}
#main-quiz-holder a {
text-decoration: none;
}
#results-container, #intro-container {
width: 500px;
text-align: center;
}
.questionContainer {
width: 560px;
min-height: 400px;
padding: 20px;
overflow: auto;
margin: auto;
}
.questionContainer .question, h2.qTitle {
margin: 10px 0 20px 0;
font-size: 26px;
font-weight: normal;
}
h2.qTitle {
font-size: 32px;
margin-top: 30px;
}
.questionContainer ul.answers {
margin: 0px;
padding: 5px;
list-style: none;
}
.questionContainer ul.answers li {
padding: 5px 50px;
margin: 12px 0;
color: #4c4c4c;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
-webkit-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
-moz-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
background: #f6f6f6;
background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#d4d4d4));
background: -webkit-linear-gradient(#f6f6f6, #d4d4d4);
background-image: -moz-linear-gradient(top, #f6f6f6, #d4d4d4);
background-image: -moz-gradient(top, #f6f6f6, #d4d4d4);
border: 1px solid #a1a1a1;
}
.questionContainer ul.answers li.selected {
background: #6fb2e5;
box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
}
.questionContainer .prev, .questionContainer .next {
height: 19px;
font-size: 16px;
padding: 5px 10px;
color: #4c4c4c;
border-radius: 4px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
background: #6fb2e5;
box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
-moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;
}
.questionContainer .next {
background: #77d125;
box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
-o-box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
-webkit-box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
-moz-box-shadow: 0 1px 5px #3caa00, inset 0 10px 20px #c9ffb6;
}
#progressKeeper {
width: 553px;
margin: 0px 12px;
box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-o-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-webkit-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
-moz-box-shadow:0 1px 5px #D9D9D9,inset 0 10px 20px #F1F1F1;
border-radius: 2px;
border:1px solid #dedede;
position: absolute;
bottom: 10px;
left: 10px;
}
#progress {
width: 0;
height: 20px;
color: #4c4c4c;
background: #f6f6f6;
background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#d4d4d4));
background: -webkit-linear-gradient(#f6f6f6, #d4d4d4);
background-image: -moz-linear-gradient(top, #f6f6f6, #d4d4d4);
background-image: -moz-gradient(top, #f6f6f6, #d4d4d4);
}
#resultKeeper {
margin: 10px;
text-align: center;
overflow: auto;
}
#notice {
position: absolute;
bottom: 40px;
right: 20px;
}
.questionContainer .prev { float: left;}
.questionContainer .next, .questionContainer.btnShowResult { float: right;
.questionContainer .clear { clear: both;
.hide { display: none;
.btnPrev {
padding-left: 24px;
background: url(img/back.png) left no-repeat;
}
.btnNext {
padding-right: 24px;
background: url(img/forward.png) right no-repeat;
}
.btnShowResult{
padding-left: 24px;
background: url(img/confirm.png) left no-repeat;
}
.btnStart {
display: block;
margin: 40px auto 0 auto;
}
.btnContainer {
margin: 20px 0 30px 0;
padding: 5px;
}
.resultRow {
width: 110px;
margin: 10px 25px;
float: left;
}
.correct, .wrong {
height: 19px;
font-size: 16px;
padding: 5px 15px;
color: #4c4c4c;
border-radius: 4px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
-webkit-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
-moz-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), inset 0 0 6px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
}
.correct {
background: #b2d840;
background: -webkit-gradient(linear, left top, left bottom, from(#b2d840), to(#90b61e));
background: -webkit-linear-gradient(#b2d840, #90b61e);
background-image: -moz-linear-gradient(top, #b2d840, #90b61e);
background-image: -moz-gradient(top, #b2d840, #90b61e);
border: 1px solid #5d8300;
}
.wrong {
background: #e84545;
background: -webkit-gradient(linear, left top, left bottom, from(#e84545), to(#c62323));
background: -webkit-linear-gradient(#e84545, #c62323);
background-image: -moz-linear-gradient(top, #e84545, #c62323);
background-image: -moz-gradient(top, #e84545, #c62323);
border: 1px solid #930000;
color: #F1F1F1;
}
.correct span {
padding: 0 20px;
background: url(img/confirm.png) left no-repeat;
}
.wrong span {
padding: 0 20px;
background: url(img/delete.png) left no-repeat;
}
#answer-key {
text-align: center;
width: 300px;
padding: 15px;
margin: 0 auto;
clear: both;
font-size: 16px;
}
|
Со всем, кроме JavaScript, наш движок должен выглядеть так, как показано ниже:
Взаимодействие JavaScript
JavaScript, хотя и короче, чем вы ожидаете, является сердцем и душой мини-движка. Как я уже упоминал ранее, это довольно красивый альфа-код, поэтому перед развертыванием обязательно очистите его.
Как всегда, давайте рассмотрим код по одному разделу за раз.
Во-первых, основы
Сначала мы разберемся с некоторыми шаблонными элементами.
1
2
3
|
$(function(){
// Everything that follows goes in here
})
|
Давайте поместим весь наш код в блок выше — самозапускающуюся анонимную функцию. Таким образом, наши переменные будут хорошо распределены по именам, что позволит избежать конфликтов имен в будущем.
1
2
3
4
5
6
7
8
9
|
var progress = $(‘#progress’),
progressKeeper = $(‘#progressKeeper’),
notice = $(«#notice»),
progressWidth = 548,
answers= kroggy.answers,
userAnswers = [],
questionLength= answers.length,
questionsStatus = $(«#questionNumber»)
questionsList = $(«.question»);
|
И это переменные, которые мы будем использовать сегодня. Имена должны сделать их цель совершенно очевидной. Мы также кешируем несколько важных элементов для последующего использования. Важно отметить, как мы присваиваем answers
значение kroggy.answers
. Я объясню это прямо ниже.
1
|
var kroggy = { answers: [ ‘b’, ‘d’, ‘a’, ‘c’, ‘a’, ‘d’, ‘b’, ‘a’, ‘d’, ‘a’, ‘d’, ‘c’, ‘a’, ‘b’, ‘d’ ] }
|
Если вы посмотрели демо, вы заметите небольшой тег сценария, содержащий приведенный выше код. Здесь мы создаем объект с именем kroggy
и kroggy
ключ ответа в нашу викторину в массиве с именем answers
. Это значение, которое мы присвоили переменной, answers
ранее.
Создание вспомогательных функций
Прежде чем приступить к созданию реального кода, давайте сначала разберемся с несколькими небольшими вспомогательными методами. В порядке возрастания важности.
1
2
3
4
|
function roundReloaded(num, dec) {
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
return result;
}
|
Я не смог найти достаточно приличный метод округления, предопределенный в JavaScript, поэтому я решил кое-что собрать.
Вам нужно будет отправить значение и количество значащих цифр, чтобы округлить, и функция сделает все остальное. Поскольку логика невероятно проста, я позволю вам разобрать все остальное.
01
02
03
04
05
06
07
08
09
10
11
|
function judgeSkills(score) {
var returnString;
if (score==100) returnString = «Albus, is that you?»
else if (score>90) returnString = «Outstanding, noble sir!»
else if (score>70) returnString = «Exceeds expectations!»
else if (score>50) returnString = «Acceptable. For a muggle.»
else if (score>35) returnString = «Well, that was poor.»
else if (score>20) returnString = «Dreadful!»
else returnString = «For shame, troll!»
return returnString;
}
|
Комментарии на экране результатов принесли много веселых комментариев и забав среди участников викторины, и именно здесь происходит волшебство.
Опять же, довольно простое программирование, которое здесь происходит. Функция принимает оценку в качестве параметра и возвращает комментарий.
Здесь условия произвольны, но я думаю, что они неплохо справляются. Не стесняйтесь поиграть с условиями в вашем проекте.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function checkAnswers() {
var resultArr = [],
flag = false;
for (i=0; i<answers.length; i++) {
if (answers[i] == userAnswers[i]) {
flag = true;
}
else {
flag = false;
}
resultArr.push(flag);
}
return resultArr;
}
|
Это основная функция, которая оценивает процесс проверки. По сути, мы сравниваем массив с другим массивом и помещаем логический результат в другой массив. Просто как тот.
В приведенном выше коде answers
и userAnswers
являются глобальными переменными, которые мы объявили ранее. В то время как первый инициализируется и ему присваивается значение на раннем этапе, последний не будет изменяться до последней минуты, т.е. когда на последний вопрос викторины будет дан ответ.
Мы просто перебираем массив и для каждого элемента проверяем ожидаемый ответ по отношению к ответу пользователя. Если все получилось, поместите значение true
в наш массив результатов. Остальное, нажми false
.
Когда оценка завершена, мы возвращаем массив результатов, чтобы его можно было проанализировать позже.
Подключение обработчиков событий
1
2
3
4
5
6
|
$(‘.btnStart’).click(function(){
$(this).parents(‘.questionContainer’).fadeOut(500, function(){
$(this).next().fadeIn(500, function(){ progressKeeper.show(); });
});
return false;
});
|
Сначала давайте сделаем начальный вводный слайд. Помните, что все, что мы делаем, это отображаем изображение, обернутое якорем? Это якорь, к которому мы подключаем вышеуказанное событие.
По сути, мы скрываем родителя элемента ссылки, по которому щелкнули, а затем исчезаем в ближайшем родственнике родителя.
Если вы заметили, что мы не добавляем каждую из этих строк отдельно, хорошо. Вы обращаете внимание. В приведенном выше методе мы передаем следующий шаг процесса в качестве обратного вызова для функции анимации. Таким образом, каждая из этих анимаций происходит одна за другой, а не одновременно, что является поведением по умолчанию. Это позволяет нам создать плавный интерфейсный эффект.
1
2
3
4
5
6
7
8
|
$(‘.btnPrev’).click(function(){
notice.hide();
$(this).parents(‘.questionContainer’).fadeOut(500, function(){
$(this).prev().fadeIn(500)
});
progress.animate({ width: progress.width() — Math.round(progressWidth/questionLength), }, 500 );
return false;
});
|
Приведенный выше код обрабатывает предыдущую кнопку. Функциональность слайдера очень похожа. Скройте родительский контейнер и добавьте next
элемент в дерево. В основном тот же код, что и в предыдущем разделе, но с обратным направлением.
После этого мы решаем индикатор выполнения дальше. Здесь мы просто анимируем ширину индикатора выполнения. Здесь нет большого расчета — разделите ширину индикатора выполнения на число вопросов.
Так как это предыдущая кнопка, нам нужно вычесть дробь из общей текущей ширины бара, что мы и делаем. Может выглядеть немного сложно, но поверьте мне, это довольно просто.
01
02
03
04
05
06
07
08
09
10
11
12
|
$(‘.btnNext’).click(function(){
var tempCheck = $(this).parents(‘.questionContainer’).find(‘input[type=radio]:checked’);
if (tempCheck.length == 0) {
notice.fadeIn(300);return false;
}
notice.hide();
$(this).parents(‘.questionContainer’).fadeOut(500, function(){
$(this).next().fadeIn(500);
});
progress.animate({ width: progress.width() + Math.round(progressWidth/questionLength), }, 500 );
return false;
});
|
У нас здесь происходит еще несколько вещей, так что обратите внимание.
Сначала мы создаем быструю переменную и присваиваем ей массив всех переключателей, которые были проверены. Селектор input[type=radio]:checked
помогает нам сделать это с минимальными усилиями.
Теперь мы можем проверить длину этого массива. Если он равен нулю, это означает, что пользователь не выбрал опцию, и поэтому мы можем показать уведомление об ошибке, упрекающее пользователя за это. Что и происходит внутри оператора if. Мы немедленно выходим из функции, используя return false
.
Если мы прошли предыдущий шаг, пришло время продолжить. Теперь мы можем скрыть уведомление, если оно было отображено ранее.
Логика ползунка снова вступает в игру — скрыть текущий контейнер и исчезнуть в следующем контейнере. Мы видели это уже несколько раз, и я не думаю, что мне нужно перефразировать это.
Наконец, мы работаем с индикатором выполнения. Как и в последнем блоке, мы рассчитываем дополнительную ширину, а затем добавляем ее к текущей ширине индикатора выполнения. Имейте в виду, что в предыдущем блоке мы вычитали его из текущей ширины. В этот блок мы добавляем его, потому что мы переходим к следующему вопросу, и поэтому индикатор выполнения продвигается вперед.
Сбор ответов пользователя
1
2
3
|
$(‘.btnShowResult’).click(function(){
// Stuff goes in here
});
|
Ааа, блудный обработчик событий, который лежит в основе всего. Мы разделим процесс на две части:
- Сбор данных
- Оценка и отображение результатов
Мы собираемся заняться первым в этом разделе. Имейте в виду, что весь JavaScript ниже идет в обработчике выше.
1
2
3
4
5
6
7
8
|
var tempCheck = $(this).parents(‘.questionContainer’).find(‘input[type=radio]:checked’);
if (tempCheck.length == 0) {
notice.fadeIn(300);return false;
}
var tempArr = $(‘input[type=radio]:checked’);
for (var i = 0, ii = tempArr.length; i < ii; i++) {
userAnswers.push(tempArr[i].getAttribute(‘data-key’));
}
|
Первые несколько строк должны выглядеть очень похожими. Это потому, что эти строки заимствованы непосредственно из обработчика следующей кнопки. Мы проверяем, была ли выбрана опция, и если нет, отображаем сообщение об ошибке.
После того, как мы проверили бизнес на обезьян, мы в основном создали массив всех отмеченных флажков. Затем мы перебираем массив и собираем атрибут data-key
каждого элемента. Помните это? Мы добавили их к переключателям во время фазы HTML и указали на положение флажка с алфавитной точки зрения. То есть первый вариант — это a, второй — это b и так далее.
Здесь мы просто собираем выбранные ответы. Познакомиться с выбранным флажком, используя метод index
, проще, ретроспективно, но именно так я и использовал в исходной кодовой базе, и это то, что я придерживаюсь сегодня.
Мы userAnswers
эти значения в глобальную переменную userAnswers
[в пределах нашей области видимости], чтобы оценить ее позже.
Оценка результатов
Теперь, когда мы собрали наши данные, мы можем быстро проанализировать и отобразить результаты. Давайте разберемся с этим небольшими кусками.
1
2
3
4
5
6
|
progressKeeper.hide();
var results = checkAnswers(),
resultSet = »,
trueCount = 0,
answerKey = ‘ Answers <br />’,
score;
|
Так как мы находимся в конце бизнеса, мы скрываем индикатор выполнения. Не нужно сейчас.
Мы также создаем кучу переменных, чтобы помочь нам отслеживать вещи внутри. Переменная results
содержит массив результатов, который возвращает checkAnswers
. Мы будем использовать его для отображения финального экрана. Цель остальных из них скоро станет очевидной.
1
2
3
4
5
6
|
for (var i = 0, ii = results.length; i < ii; i++){
if (results[i] == true) trueCount++;
resultSet += ‘<div class=»resultRow»> Question #’ + (i + 1) + (results[i]== true ? «<div class=’correct’><span>Correct
}
score = roundReloaded(trueCount / questionLength*100, 2); |
Теперь, когда у нас есть данные, давайте сделаем с ними несколько полезных вещей. Сначала мы перебираем результаты, чтобы узнать количество вопросов, которые пользователь получил правильно. Мы будем использовать это число для подсчета очков позже.
Далее, мы возьмем на себя ответственность за получение элегантных результатов. Поскольку мы уже в цикле, мы будем использовать его для рендеринга всего остального. Мы показываем номер вопроса и, в зависимости от того, правильно ли пользователь понял этот конкретный вопрос, помещаем в него другой HTML. Помните, CSS для correct and wrong
классов? Они используются здесь.
Поскольку нам нужно отобразить ключ ответа, мы используем тот же цикл, чтобы пройти по answers
массиву и отобразить правильный ответ.
И, наконец, мы получаем округленный результат с помощью roundReloaded
функции, которую мы создали ранее, и сохраняем значение в score
переменной.
Отображение результатов
Мясо нашей работы сделано. Нам просто нужно обернуть несколько вещей и отобразить результаты.
1
2
3
4
5
6
7
|
answerKey = "<div id='answer-key'>" + answerKey + "</div>" ; resultSet = '<h2 class="qTitle">' +judgeSkills(score) + ' You scored ' +score+ '%</h2>' + resultSet + answerKey; $( '#resultKeeper' ).html(resultSet).show(); $( this ).parents( '.questionContainer' ).fadeOut(500, function (){ $( this ).next().fadeIn(500); });
return false;
|
Здесь происходят очень простые вещи. Во-первых, мы answerKey
заключаем значение в div с идентификатором, answer-key
чтобы лучше его стилизовать.
Далее мы создаем заголовок экрана результатов. Мы используем judgeSkills
метод, чтобы создать соответствующие комментарии, а затем несколько шаблонов, чтобы упомянуть счет. Мы добавляем эти значения resultSet
к конечной строке и добавляемanswerKey
На этом мы завершаем окончательный HTML-код, который необходимо поместить на экран результатов. Мы просто заменяем HTML, используя наш предварительно сгенерированный HTML. Теперь, когда все готово, мы исчезаем с предпоследнего слайда и исчезаем на экране результатов.
Финальные биты домашнего хозяйства
У нас есть несколько простых дел по ведению домашнего хозяйства, которые нам нужно обернуть.
1
2
3
|
progressKeeper.hide(); notice.hide(); $( "#main-quiz-holder input:radio" ).attr( "checked" , false ); |
Во время инициализации на заставке нам не нужны индикатор выполнения или сообщение об ошибке. Давайте пойдем дальше и спрятать это изначально. Кроме того, Firefox, по какой-то причине, имеет тенденцию «запоминать» ответы, поэтому давайте стереть их полностью — каждый флажок не будет выбран при обновлении страницы.
1
2
3
4
|
$( '.answers li input' ).click( function () { $( this ).parents( '.answers' ).children( 'li' ).removeClass( "selected" ); $( this ).parents( 'li' ).addClass( 'selected' ); });
|
Вот кое-что, чтобы осветить выбранный вариант. Обратите немного внимания, потому что здесь все немного рискованно.
Когда флажок установлен, мы переходим к родительскому элементу списка и удаляем selected
класс из всех его дочерних элементов li. После этого мы даем родителю флажка, на который нажали флажок, selected
класс, чтобы он светился ярко-синим цветом.
И, наконец, чтобы завершить работу слайдера, вам нужно скрыть все, кроме первого слайда. Вы можете использовать JavaScript, чтобы сделать это, или более простой способ CSS — добавить display:none
объявление для всех, кроме первого слайда. Это именно то, что hide
класс делает в демоверсии.
Что мы построили
Завершение и дружественный штекер
Уф!Это было довольно долго, не так ли? Я вижу там подлую шутку, но я воздержусь!
Если вам понравился тестовый движок, который мы создали сегодня, я создал еще более продвинутую версию. Один с отзывами об ответах на странице результатов, социальным обменом, счетчиками вопросов и многим другим. Это так хорошо, что даже сайты Tuts + будут использовать его!
Взгляните на jQuizzy , мой новый движок для викторин. Тебе это понравится, обещаю!
Во всяком случае, мы закончили здесь. Мы рассмотрели создание механизма тестирования с нуля, охватывающего все аспекты от HTML до CSS и JavaScript. Мы рассмотрели изящные методы для стилизации элементов и программирования взаимодействия через JavaScript. Надеюсь, вы нашли это интересным и полезным.
Если у вас возникнут какие-либо проблемы, оставьте мне комментарий. Большое спасибо за чтение!