重叠范围输入。在点击更改输入时具有最接近的值

时间:2016-06-11 22:36:07

标签: javascript input

我有两个重叠的范围输入,这会产生多范围输入效果。

我想要它,以便每当对其中任何一个进行单击时,更改具有与新单击的值最接近的值的输入。不完全确定如何解决这个问题。

我怎么能这样做?



(function() {
  "use strict";

  var supportsMultiple = self.HTMLInputElement && "valueLow" in HTMLInputElement.prototype;

  var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");

  self.multirange = function(input) {
    if (supportsMultiple || input.classList.contains("multirange")) {
      return;
    }

    var values = input.getAttribute("value").split(",");
    var max = +input.max || 100;
    var ghost = input.cloneNode();

    input.classList.add("multirange", "original");
    ghost.classList.add("multirange", "ghost");

    input.value = values[0] || max / 2;
    ghost.value = values[1] || max / 2;

    input.parentNode.insertBefore(ghost, input.nextSibling);

    Object.defineProperty(input, "originalValue", descriptor.get ? descriptor : {
      // Dang you Safari >:(
      get: function() {
        return this.value;
      },
      set: function(v) {
        this.value = v;
      }
    });

    Object.defineProperties(input, {
      valueLow: {
        get: function() {
          return Math.min(this.originalValue, ghost.value);
        },
        set: function(v) {
          this.originalValue = v;
        },
        enumerable: true
      },
      valueHigh: {
        get: function() {
          return Math.max(this.originalValue, ghost.value);
        },
        set: function(v) {
          ghost.value = v;
        },
        enumerable: true
      }
    });

    if (descriptor.get) {
      // Again, fuck you Safari
      Object.defineProperty(input, "value", {
        get: function() {
          return this.valueLow + "," + this.valueHigh;
        },
        set: function(v) {
          var values = v.split(",");
          this.valueLow = values[0];
          this.valueHigh = values[1];
        },
        enumerable: true
      });
    }

    function update() {
      ghost.style.setProperty("--low", input.valueLow * 100 / max + 1 + "%");
      ghost.style.setProperty("--high", input.valueHigh * 100 / max - 1 + "%");
    }

    input.addEventListener("input", update);
    ghost.addEventListener("input", update);

    update();
  }

  multirange.init = function() {
    Array.from(document.querySelectorAll("input[type=range][multiple]:not(.multirange)")).forEach(multirange);
  }

  if (document.readyState == "loading") {
    document.addEventListener("DOMContentLoaded", multirange.init);
  } else {
    multirange.init();
  }

})();

@supports (--css: variables) {
  input[type="range"].multirange {
    -webkit-appearance: none;
    padding: 0;
    margin: 0;
    display: inline-block;
    vertical-align: top;
    width: 250px;
    margin-top: 50px;
    margin-left: 50px;
    background: lightblue;
  }
  input[type="range"].multirange.original {
    position: absolute;
  }
  input[type="range"].multirange.original::-webkit-slider-thumb {
    position: relative;
    z-index: 2;
  }
  input[type="range"].multirange.original::-moz-range-thumb {
    transform: scale(1);
    /* FF doesn't apply position it seems */
    G z-index: 1;
  }
  input[type="range"].multirange::-moz-range-track {
    border-color: transparent;
    /* needed to switch FF to "styleable" control */
  }
  input[type="range"].multirange.ghost {
    position: relative;
    background: var(--track-background);
    --track-background: linear-gradient(to right, transparent var(--low), var(--range-color) 0, var(--range-color) var(--high), transparent 0) no-repeat 0 45% / 100% 40%;
    --range-color: hsl(190, 80%, 40%);
  }
  input[type="range"].multirange.ghost::-webkit-slider-runnable-track {
    background: var(--track-background);
  }
  input[type="range"].multirange.ghost::-moz-range-track {
    background: var(--track-background);
  }
}

<input type="range" multiple value="10,80" />
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:6)

您必须捕获元素上的鼠标事件,并计算它与高标记与低标记的接近程度,并根据该标记决定更新哪一个。此外,因为这些是两个堆叠的输入元素,您可能必须手动将事件传递给低范围输入。

Here's my go创建这样一个函数:

function passClick(evt) {
  // Are the ghost and input elements inverted? (ghost is lower range)
  var isInverted = input.valueLow == ghost.value;
  // Find the horizontal position that was clicked (as a percentage of the element's width) 
  var clickPoint = evt.offsetX / this.offsetWidth;
  // Map the percentage to a value in the range (note, assumes a min value of 0)
  var clickValue = max * clickPoint;

  // Get the distance to both high and low values in the range
  var highDiff = Math.abs(input.valueHigh - clickValue);
  var lowDiff = Math.abs(input.valueLow - clickValue);

  if (lowDiff < highDiff && !isInverted || (isInverted && lowDiff > highDiff)) {
    // The low value is closer to the click point than the high value
    // We should update the low value input
    var passEvent = new MouseEvent("mousedown", {screenX: evt.screenX, clientX: evt.clientX});
    // Pass a new event to the low "input" element (which is obscured by the
    // higher "ghost" element, and doesn't get mouse events outside the drag handle
    input.dispatchEvent(passEvent);
    // The higher "ghost" element should not respond to this event
    evt.preventDefault();
    return false;
  }
  else {
    console.log("move ghost");
    // The high value is closer to the click point than the low value
    // The default behavior is appropriate, so do nuthin
  }
}

ghost.addEventListener("mousedown", passClick);

我将此代码放在样本中input.addEventListener("input", update);行的正上方,似乎可以正常工作。查看我的fiddle

但有些附带条件:

  • 我只在Chrome中测试过。根据我复制事件的方式,IE可能会遇到一些麻烦。 可能使用dispatchEvent以外的机制......如fireEvent或其他。
  • 最初我编码它假设“ghost”元素始终跟踪高范围。我已经更新了一些内容,以便在ghost元素值较低时反转事件调度 - 但是我加快了速度。

答案 1 :(得分:2)

这里有一些你可以使用的简单方法。虽然您可能想要自定义样式。 我正在根据它与光标的接近程度来改变滑块元素的z-index JSFiddle

HTML

<input id='a' type='range' />
<input id='b' type='range' />
<label role='info'></label>

JS

var a = document.getElementById('a');
var b = document.getElementById('b');

a.onmousemove = function(e) {
    MouseMove.call(a, e);
};
b.onmousemove = function(e) {
    MouseMove.call(b, e);
};

var MouseMove = function(eventArg) 
{

    var max = parseInt(a.max),
        min = parseInt(a.min),
        diff = max - min,
        clickPoint = eventArg.offsetX / a.offsetWidth,
        clickPointVal = parseInt(diff * clickPoint) + min;

    /* absolute distance from respective slider values */
    var da = Math.abs(a.value - clickPointVal),
        db = Math.abs(b.value - clickPointVal);

    // Making the two sliders appear above one another only when no mouse button is pressed, this condition may be removed at will
    if (!eventArg.buttons) 
    {
        if (da < db) 
        {
            a.style.zIndex = 2;
            b.style.zIndex = 1;
        } 
        else if (db < da)
        {
            b.style.zIndex = 2;
            a.style.zIndex = 1;
        }
    }
    document.querySelector('label').innerHTML = 'Red: ' + a.value + ', Green: ' + b.value + ', X: ' + eventArg.clientX;
}

CSS

input {
    margin: 0px;
    position: absolute;
    left: 0px;
}

label {
    display: inline-block;
    margin-top: 100px;
}

#a {
    z-index: 2;
}

#b {
    z-index: 1;
}