2016-06-11 13 views
5

Mam dwa nakładające się wejścia zakresów, co tworzy efekt wejścia o wielu zakresach.Nakładające się wejścia zakresów. Po wprowadzeniu zmiany kliknięcia o najbliższej wartości

chcę go tak, że gdy jedno kliknięcie odbywa się na jeden z nich, wejście z najbliższą wartość do nowo kliknął wartości ulega zmianie. Nie do końca pewna, jak to osiągnąć.

Jak mogę to zrobić?

(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" />

+0

Może mógłbym zwrócić uwagę na to: http://refreshless.com/nouislider/. Spójrz na przykład na górze. Przesłałem kilka wniosków o wyciągnięcie (w oddziale, który w pewnym momencie przejdzie w stan stabilny), które umożliwiają dokładnie tę samą funkcjonalność dla większej liczby uchwytów (kliknięcie suwaka powoduje przesunięcie uchwytu bliżej kliknięcia - nawet jeśli nakładają się dwa lub trzy uchwyty (sama pozycja), rączka, która może się poruszać, będzie się poruszać). Nie jestem pewien, czy rozważysz użycie innej biblioteki. Licencja to WTFPL. – xnakos

+0

Dzięki, ale wolę używać natywnego wejścia [type = range]. – ditto

+0

OK. Inne pytanie. W twoim przykładzie lewy uchwyt nie może być teraz przeciągnięty, czy mam rację? – xnakos

Odpowiedz

6

Będziesz musiał przechwycić zdarzenie myszy na elemencie i obliczyć, jak blisko jest do wysokiego wskaźnika vs. niskiego markera i zdecydować, który z nich Aktualizacja oparta na tym. Ponadto, ponieważ są to dwa ułożone elementy wejściowe, prawdopodobnie będziesz musiał ręcznie przekazać zdarzenie do wejścia o niskim zasięgu.

Here's my go stworzenie takiej funkcji:

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); 

umieścić ten kod bezpośrednio nad linią w próbce input.addEventListener("input", update); i wydaje się działać. Zobacz mój fiddle.

Niektóre zastrzeżeniami jednak:

  • I tylko przetestowane w Chrome. IE może mieć pewne problemy związane z replikacją zdarzenia. To może użyć mechanizmu innego niż dispatchEvent ... jak fireEvent lub coś podobnego.
  • Początkowo zakodowałem go, zakładając, że element "duch" zawsze śledził wysoki zakres. Od czasu aktualizacji rzeczy zmieniłem sposób wyświetlania zdarzeń, gdy element ghost ma niższą wartość - ale przeleciałem przez to.
+0

Jestem w trakcie korzystania z dwóch zakresów wejściowych i udało mi się go działa na wszystkich oprócz firefox (v55.0.3). Próbowałem Twojego rozwiązania bez powodzenia, ale myślę, że jesteś na czymś, co może zadziałać, jeśli trochę go podkręcę – doz87

2

Oto coś prostego, można użyć. Chociaż możesz chcieć dostosować styl. Zmieniam z-index elementu suwaka na podstawie jego bliskości do kursora.
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; 
} 
+0

Podoba mi się ten pomysł i sam go zaimplementowałem, aby poradzić sobie z niezgodnością firefox dla wielu zakresów wejściowych, ale sugeruję pewne zmiany w jaki sposób radzisz sobie z identyfikacją najbliższego uchwytu, tak jak zrobił to @theDemveloper, powinieneś przełączyć na używanie procentów. Nie wiem, dlaczego używasz '* 100/160' – doz87

+0

Próbowałem edytować twoje rozwiązanie, ale jest zbyt wiele do zmiany, więc spójrz na poniższy przykład, jeśli chcesz zobaczyć coś, co jest zgodne z przeglądarką [ Przykład Codepen] (https://codepen.io/doz87/pen/pWbywG) – doz87

+0

FYI - Mój przykład zawiera ograniczenie, że min i max nie mogą być większe niż 10% od siebie – doz87