/** * @file * JavaScript behaviors for range element integration. */ (function ($, Drupal) { 'use strict'; /** * Display HTML5 range output in a left/right aligned number input. * * @type {Drupal~behavior} */ Drupal.behaviors.webformRangeOutputNumber = { attach: function (context) { $(context).find('.js-form-type-range').once('webform-range-output-number').each(function () { // Handle browser that don't support the HTML5 range input. if (Modernizr.inputtypes.range === false) { return; } var $element = $(this); var $input = $element.find('input[type="range"]'); var $output = $element.find('input[type="number"]'); if (!$output.length) { return; } // Set output value. $output.val($input.val()); // Sync input and output values. $input.on('input', function () { $output.val($input.val()); }); $output.on('input', function () { $input.val($output.val()); }); }); } }; /** * Display HTML5 range output in a floating bubble. * * @type {Drupal~behavior} * * @see https://css-tricks.com/value-bubbles-for-range-inputs/ * @see https://stackoverflow.com/questions/33794123/absolute-positioning-in-relation-to-a-inputtype-range */ Drupal.behaviors.webformRangeOutputBubble = { attach: function (context) { $(context).find('.js-form-type-range').once('webform-range-output-bubble').each(function () { // Handle browser that don't support the HTML5 range input. if (Modernizr.inputtypes.range === false) { return; } var $element = $(this); var $input = $element.find('input[type="range"]'); var $output = $element.find('output'); var display = $output.attr('data-display'); if (!$output.length) { return; } $element.css('position', 'relative'); $input.on('input', function () { var inputValue = $input.val(); // Set output text with prefix and suffix. var text = ($output.attr('data-field-prefix') || '') + inputValue + ($output.attr('data-field-suffix') || ''); $output.text(text); // Set output top position. var top; if (display === 'above') { top = $input.position().top - $output.outerHeight() + 2; } else { top = $input.position().top + $input.outerHeight() + 2; } // It is impossible to accurately calculate the exact position of the // range's buttons so we only incrementally move the output bubble. var inputWidth = $input.outerWidth(); var buttonPosition = Math.floor(inputWidth * (inputValue - $input.attr('min')) / ($input.attr('max') - $input.attr('min'))); var increment = Math.floor(inputWidth / 5); var outputWidth = $output.outerWidth(); // Set output left position. var left; if (buttonPosition <= increment) { left = 0; } else if (buttonPosition <= increment * 2) { left = (increment * 1.5) - outputWidth; if (left < 0) { left = 0; } } else if (buttonPosition <= increment * 3) { left = (increment * 2.5) - (outputWidth / 2); } else if (buttonPosition <= increment * 4) { left = (increment * 4) - outputWidth; if (left > (increment * 5) - outputWidth) { left = (increment * 5) - outputWidth; } } else if (buttonPosition <= inputWidth) { left = (increment * 5) - outputWidth; } // Also make sure to include the input's left position. left = Math.floor($input.position().left + left); // Finally, position the output. $output.css({top: top, left: left}); }) // Fake a change to position output at page load. .trigger('input'); // Add fade in/out event handlers if opacity is defined. var defaultOpacity = $output.css('opacity'); if (defaultOpacity < 1) { // Fade in/out on focus/blur of the input. $input.on('focus mouseover', function () { $output.stop().fadeTo('slow', 1); }); $input.on('blur mouseout', function () { $output.stop().fadeTo('slow', defaultOpacity); }); // Also fade in when focusing the output. $output.on('touchstart mouseover', function () { $output.stop().fadeTo('slow', 1); }); } }); } }; })(jQuery, Drupal);