Приветствую всех!
В этот раз речь пойдет о создании калькулятора для расчета взвешенных оценок. Такие оценки часто используют при проведении различных тестирований и сравнений.
Например, вы хотите сравнить несколько мобильных телефонов между собой. Выбираете параметры, которые для вас важны (пусть это будут: цена, размер дисплея и вес) и для каждого из них выставляете оценку.
Естественно некоторые параметры будут важнее чем другие. Например, цена важнее веса. И вы хотите это учесть.
Для этого используются так называемые взвешенные оценки. Каждому параметру присваивается свой вес, который отражает его важность (чем больше, тем важнее), а затем рассчитывается суммарная оценка.
В суммарную оценку входит сумма оценок всех параметров, умноженных на их вес.
При этом удобно, чтобы сумма весов всех параметров равнялась 1 (100%). Например, цена – 50%, размер дисплея – 40%, вес – 10%.
Теперь рассмотрим пример создания такого калькулятора для 3-х параметров.
Нам понадобятся 3 поля для ввода оценок каждого из параметров и 3 слайдера (slider) для задания их весов.
В данном случае слайдеры создадут очень наглядное представление весов параметров. К тому же мы сделаем их связанными между собой и пользователю не придется следить за тем, чтобы сумма их значений была равна 100%.
Чтобы показать как будет выглядеть окончательный результат, я сделал демонстрационную страничку.
Demo
Кроме того, вы можете скачать архив с исходниками этого примера и запустить его локально.
SourceСразу хочу обратить ваше внимание на чекбоксы (checkbox) справа от слайдеров. Они служат для того, чтобы заблокировать изменение соответствующего слайдера. Т.к. слайдеров у нас три, то при перемещении одного из них, будут изменяться значения остальных.
Поэтому принцип работы следующий. Выставляете нужное значение для первого слайдера, блокируете его, выставляете значения остальных слайдеров, вводите оценки и сразу видите результат.
Структура и принцип работы калькулятора
Наше приложение состоит из одной HTML страницы, трёх JavaScript файлов (из них два – это библиотеки jQuery и jQuery UI) и 2-х файлов с CSS стилями.
Начнём со страницы – index.html.
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <title>Каклькулятор, взвешенные оценки</title>
- <meta http-equiv="content-type" content="text/html;charset=utf-8" />
- <meta http-equiv="Content-Style-Type" content="text/css" />
- <meta name="description" content="Использование ползунков (slider) для создания калькулятора" />
- <meta name="keywords" content="калькулятор, ползунки, slider, взвешенная оценка" />
- <link rel="stylesheet" type="text/css" href="css/ui-lightness/jquery-ui-1.7.2.custom.css" media="all" />
- <link rel="stylesheet" type="text/css" href="styles.css" media="all" />
- </head>
- <body>
- <div id="parameter_1" class="calc_row">
- <input type="text" name="val_1" id="val_1" class="val_field" value="0" />
- <div id="weight_1" class="slider"></div>
- <div id="sv_1" class="slider_val"></div>
- <input type="checkbox" class="sl_enable" />
- </div>
- <div id="parameter_2" class="calc_row">
- <input type="text" name="val_2" id="val_2" class="val_field" value="0" />
- <div id="weight_2" class="slider"></div>
- <div id="sv_2" class="slider_val"></div>
- <input type="checkbox" class="sl_enable" />
- </div>
- <div id="parameter_3" class="calc_row">
- <input type="text" name="val_3" id="val_3" class="val_field" value="0" />
- <div id="weight_3" class="slider"></div>
- <div id="sv_3" class="slider_val"></div>
- <input type="checkbox" class="sl_enable" />
- </div>
- <div id="results"></div>
- <script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
- <script type="text/javascript" src="js/jquery-ui-1.7.2.custom.min.js"></script>
- <script type="text/javascript" src="js/main.js"></script>
- </body>
- </html>
* This source code was highlighted with Source Code Highlighter.
В заголовке страницы мы подключили CSS файлы (строки 15 и 16). Первый распространяется вместе с jQuery UI и используется для оформления слайдеров, второй – используется для оформления страницы.
Сама страница содержит три практически одинаковых блока. В каждый из них входят:
— текстовое поле — задаёт оценку параметра;
— слайдер – указывает вес параметра;
— текстовая надпись – отображает текущее значение слайдера;
— чекбокс (checkbox) – используется для включения/отключения слайдера.
За ними расположен блок, показывающий суммарную оценку (строка 42)
В конце страницы мы подключаем JavaScript файлы (строки 44-46). Сначала библиотека jQuery, затем jQuery UI и файл с нашими скриптами (main.js).
Теперь рассмотрим наш main.js. Это самая сложная и объёмная часть калькулятора. Но большая часть кода касается настройки взаимной работы слайдеров и чекбоксов.
- $(function() {
- /* создаем слайдеры */
- $(".slider").slider();
- /* устанавливаем начальные значения */
- $(".sl_enable").attr('checked', false);
- $(".slider").each(function() {
- $(this).slider('option', 'value', 33.3);
- });
- $(".slider_val").html("33.3 %");
- $('.val_field').val('0');
- calculate();
- /* минимальное и максимальное значение активных слайдеров */
- var min = 0;
- var max = 100;
- /*
- Устанавливаем обработчики чекбоксов.
- Использовать событие change не получается, т.к. в IE оно возникает
- после того как checkbox теряет фокус (т.е. после переключения
- checkbox'а нужно кликнуть где-нибудь на странице, чтобы возникло
- событие change)
- */
- $(".sl_enable").click(function() {
- var curId = getCurId(this);
- var curSlider = $('#weight_' + curId);
- if (this.checked) {
- curSlider.slider('disable');
- $("#sv_" + curId).addClass('gray');
- }
- else {
- curSlider.slider('enable');
- $("#sv_" + curId).removeClass('gray');
- }
- setMinMax();
- });
- /* обработчик событий slide слайдеров (возникает при перемещении слайдера) */
- $(".slider").bind('slide', function(event, ui) {
- //блокируем перемещение слайдера если оно выходит за допустимые пределы
- if ((ui.value > max) || (ui.value < min)) {
- return false;
- }
- updateSliders(this, ui.value);
- updateLabels();
- calculate();
- })
- .bind('slidestop', function() {
- /*
- Этот обработчик вызывается после перемещения слайдера.
- Нужен для того, чтобы обновить значение текущего слайдера
- (событие slide возникает раньше)
- */
- calculate();
- updateLabels();
- });
- /* Эта функция возвращает id текущего слайдера */
- function getCurId(elem) {
- var parentId = $(elem).parent().attr('id');
- return parentId.substring(10);
- }
- /*
- Эта функция устанавливает минимально и максимально допустимые значения
- слайдеров.
- */
- function setMinMax() {
- var enabledSliders = $(".slider[aria-disabled!=true]");
- var disabledSliders = $(".slider[aria-disabled=true]");
- // определяем сумму значений, установленных на отключенных слайдерах
- var total = 0;
- disabledSliders.each(function() {
- total += $(this).slider('option', 'value');
- });
- // задаём минимальные и максимальные значения для включенных слайдеров
- max = 100 - total;
- min = 0;
- if (enabledSliders.length == 1) {
- min = max;
- }
- }
- /*
- Изменяет положение слайдеров так чтобы сумма их значений всегда была
- равна 100
- */
- function updateSliders(curSlider, curVal) {
- var enabledSliders = $(".slider[aria-disabled!=true]");
- if (enabledSliders.length == 1) {
- return;
- }
- var newVal = (max - curVal) / (enabledSliders.length - 1);
- enabledSliders.each(function(i, slider) {
- if (slider != curSlider) {
- $(slider).slider('option', 'value', newVal);
- }
- });
- }
- /* Обновляет надписи с текущими значениями слайдеров */
- function updateLabels() {
- for (var i = 1; i <= 3; i++) {
- var curVal = $("#weight_" + i).slider('option', 'value');
- //округляем до десятых
- curVal = Math.round(curVal*10) / 10;
- $("#sv_" + i).html(curVal + " %");
- }
- }
- /* проверка значений, введенных в текстовое поле */
- var textFieldVal;
- $('.val_field').bind('keydown', function(e) {
- //сохраняем текущее значение
- textFieldVal = $(this).val();
- })
- .bind('keyup', function(e) {
- if (isNaN($(this).val())) {
- $(this).val(textFieldVal);
- }
- calculate();
- });
- /* Рассчет взвешенной оценки */
- function calculate() {
- var res = 0;
- for (var i = 1; i <= 3; i++) {
- res += $('#val_' + i).val() * $('#weight_' + i).slider('option', 'value') / 100;
- }
- //Округляем до сотых
- res = Math.round(res*100) / 100;
- $('#results').html('Взвешенная оценка = ' + res);
- }
- });
* This source code was highlighted with Source Code Highlighter.
Разберем код, начиная с функции calculate
(строки 134-145), которая выполняет расчет суммарной оценки.
Здесь мы в цикле проходим по всем полям с оценками и слайдерам. Умножаем оценку на значение, установленное на слайдере и подсчитываем общую сумму.
После этого, показываем результат пользователю.
Возвращаемся в начало файла.
Прежде всего, мы создаём слайдеры (строка 3) и устанавливаем начальные значения (строки 6-14). Все оценки ставим равными 0, а веса параметров – 33,3% (100/3).
После этого, мы устанавливаем обработчики события click
для чекбоксов. При включении чекбокса мы должны сделать соответствующий слайдер неактивным (строка 33) и пересчитать максимальные и минимальные значения для остальных слайдеров. Для этого используется функция setMinMax()
.
Рассмотрим её подробнее (строки 73-89).
Сначала мы находим все активные и отключенные слайдеры (строки 74 и 75).
Затем, рассчитываем сумму значений отключенных слайдеров и отнимаем её от 100. Полученное значение является максимально допустимым для оставшихся слайдеров.
Если у нас остался только один активный слайдер, то очевидно, что он может принять только одно значение (иначе сумма не будет равна 100). Поэтому мы присваиваем min = max
.
Переходим к обработке перемещений слайдера (событие slide
, строки 44-61). Точнее мы обрабатываем 2 события: slide
и slidestop
. Дело в том, что при возникновении события slide
окончательное значение на слайдере ещё не установлено, поэтому если не обрабатывать slidestop
, то в результате получим оценку для предыдущего положения слайдера.
При возникновении события slide
мы проверяем не выходит ли слайдер за допустимые пределы и если да – блокируем его (строки 46-48).
После этого, мы обновляем положение остальных слайдеров (с помощь функции updateSliders
), обновляем текстовые надписи и пересчитываем результат (строки 49-51).
Теперь рассмотрим функцию updateSliders
(строки 95-106). Именно она устанавливает значения слайдеров так, чтобы их сумма всегда была равна 100%.
В её параметрах мы передаём указатель на текущий слайдер (его изменять нельзя) и его значение.
Принцип расчета значений остальных слайдеров такой.
Если у нас есть только один активных слайдер – не делаем ничего, т.к. он перемещаться не может.
Если есть хотя бы один активный слайдер (кроме текущего), то рассчитываем сколько нужно прибавить к значению текущего слайдера чтобы получить максимальное значение и делим результат на количество слайдеров (строка 100).
Т.е. если у нас активны все слайдеры, и на первом мы установили значение 70, то значения второго и третьего будут равны 15.
После этого, мы обновляем значения слайдеров (строка 103).
Теперь слайдеры работают так как нужно. Нам остаётся добавить проверку оценок, введённых в текстовые поля (строки 120-131).
Для этого установим обработчики событий keydown
и keyup
для этих полей. Принцип работы следующий.
При возникновении события keydown
изменение содержимого поля ещё не произошло и мы сохраняем предыдущее значение.
А при возникновении keyup
поле содержит новое значение. С помощью функции isNaN
мы проверяем, является ли новое значение числом и если нет – возвращаем предыдущее.
Тут же мы пересчитываем суммарную оценку.
Заключение
Как видите, ничего сверхсложного. Самое главное при разработке таких приложений – правильно выбирать обработчики событий и следить за тем, какие данные в них доступны. Иначе можно получить очень интересные эффекты 🙂
Описывать подробно CSS стили я не буду. Т.к. изменение оформления слайдеров – это отдельная тема, а стили калькулятора – простые до предела.
Если есть вопросы или замечания – пишите, постараюсь ответить 😉