Калькулятор взвешенных оценок

В этот раз речь пойдет о создании калькулятора для расчета взвешенных оценок. Такие оценки часто используют при проведении различных тестирований и сравнений.

Например, вы хотите сравнить несколько мобильных телефонов между собой. Выбираете параметры, которые для вас важны (пусть это будут: цена, размер дисплея и вес) и для каждого из них выставляете оценку.

Естественно некоторые параметры будут важнее чем другие. Например, цена важнее веса. И вы хотите это учесть.

Для этого используются так называемые взвешенные оценки. Каждому параметру присваивается свой вес, который отражает его важность (чем больше, тем важнее), а затем рассчитывается суммарная оценка.

В суммарную оценку входит сумма оценок всех параметров, умноженных на их вес.

При этом удобно, чтобы сумма весов всех параметров равнялась 1 (100%). Например, цена – 50%, размер дисплея – 40%, вес – 10%.

Теперь рассмотрим пример создания такого калькулятора для 3-х параметров.

Нам понадобятся 3 поля для ввода оценок каждого из параметров и 3 слайдера (slider) для задания их весов.

В данном случае слайдеры создадут очень наглядное представление весов параметров. К тому же мы сделаем их связанными между собой и пользователю не придется следить за тем, чтобы сумма их значений была равна 100%.

Сразу хочу обратить ваше внимание на чекбоксы (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>


В заголовке страницы мы подключили 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);
  }
});


Разберем код, начиная с функции 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 стили я не буду. Т.к. изменение оформления слайдеров – это отдельная тема, а стили калькулятора – простые до предела.
Теги:
Калькулятор
Добавлено: 22 Апреля 2018 08:40:25 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...