Search code examples
javascriptjqueryjquery-uijquery-pluginsjquery-ui-slider

How do I delegate a jQuery UI slider to the DOM?


I have this range slider using the jQuery UI API, and it worked as expected but now i'm trying to delegate the slider to the DOM and no matter what I try it doesn't work. Here is my initial code (no selector delegation):

HTML [initial]

<div class="SliderContainer">
                    
    <input type="hidden" id="minWT" value="2.5" />
    <input type="hidden" id="maxWT" value="30" />

    <p class="wtNumber" id="showWT">2.5Ct - 30Ct</p>

    <div class="rangeContainer">
        <div id="WTrange"></div>
    </div>

</div>

JS [initial]

$('#WTrange').slider({
        
    range: true,
    values: [2.50, 30.00],
    min: 2.50,
    max: 30.00,
    step: 0.01,
    change:

        function(event, ui) {
            
            alert('Things have changed!');
            
        },
    slide: 

        function(event, ui) {
            
            // Prevent range thumbs from overlapping
            if ((ui.values - ui.values[1]) < 3) {

                return false;

            }
        
            $('#showWT').html(ui.values[0] + 'Ct - ' + ui.values[1] + 'Ct'); // Show value above range slider
            $('#minWT').val(ui.values[0]);
            $('#maxWT').val(ui.values[1]);
            
        }
        
});

After doing some Googling I found this potential solution. But i'm either doing something wrong or it doesn't work because I can't seem to get it right:

Failed JS Attempt 1

$('#WTrange').slider();

$(document).on('slidecreate', '#WTrange', function(event, ui) {

}).on('slider', '#WTrange', function(event, ui) {

    range: true,
    values: [2.50, 30.00],
    min: 2.50,
    max: 30.00,
    step: 0.01
        
}).on('slidechange', '#WTrange', function(event, ui) {

    alert('Things have changed!');
  
}).on('slide', '#WTrange', function(event, ui) {

  // Prevent range thumbs from overlapping
  if ((ui.values - ui.values[1]) < 3) {

      return false;

  }

    $('#showWT').html(ui.values[0] + 'Ct - ' + ui.values[1] + 'Ct'); // Show value above range slider
    $('#minWT').val(ui.values[0]);
    $('#maxWT').val(ui.values[1]);

});

When running this (in JSFiddle) I get this error:

"<a class='gotoLine' href='#35:9'>35:9</a> Uncaught SyntaxError: Unexpected token ':'"

Does anyone know the correct way of delegating this jQuery UI slider widget to the DOM?


EDIT 1

I tried reformatting it to delegate and it still didn't work:

Failed JS Attempt 2


$('#WTrange').slider();
$(document).on('slide', '#WTrange', 'option', 'range', true);
$(document).on('slide', '#WTrange', 'option', 'values' [2.50, 30.00]);
$(document).on('slide', '#WTrange', 'option', 'min', 2.50);
$(document).on('slide', '#WTrange', 'option', 'max', 30.00);
$(document).on('slide', '#WTrange', 'option', 'step', 0.01);
$(document).on('slidechange', '#WTrange', function(event, ui) {

    alert('Things have changed!');

});

$(document).on('slide', '#WTrange', function(event, ui) {

    // Prevent range thumbs from overlapping
    if ((ui.values - ui.values[1]) < 3) {
    
        return false;
    
    }
    
    $('#showWT').html(ui.values[0] + 'Ct - ' + ui.values[1] + 'Ct'); // Show value above range slider
    $('#minWT').val(ui.values[0]);
    $('#maxWT').val(ui.values[1]);

});

EDIT 2

I was told that my question wasn't clear enough. Here is a more minimal and reproducible example:

HTML on $(document).ready()

<div id="pageContent">
    <button>Show Slider</button>
</div>

NOTICE: No HTML for the jQuery UI slider

JS on $(document).ready()

$('button').on('click', function() {
    $('#pageContent').load('sliderPage.php');
});

$('#WTrange').slider({

    range: true,
    values: [2.50, 30.00],
    min: 2.50,
    max: 30.00,
    step: 0.01,
    change:

        function(event, ui) {

            alert('Things have changed!');

        },
    slide: 

        function(event, ui) {

            // Prevent range thumbs from overlapping
            if ((ui.values - ui.values[1]) < 3) {

                return false;

            }

            $('#showWT').html(ui.values[0] + 'Ct - ' + ui.values[1] + 'Ct'); // Show value above range slider
            $('#minWT').val(ui.values[0]);
            $('#maxWT').val(ui.values[1]);

        }

});

NOTICE: When the user .click()s the <button> sliderPage.php will .load()

HTML after sliderPage.php loaded on <button> click

<div id="pageContent">
    <div class="SliderContainer">

        <input type="hidden" id="minWT" value="2.5" />
        <input type="hidden" id="maxWT" value="30" />
    
        <p class="wtNumber" id="showWT">2.5Ct - 30Ct</p>
    
        <div class="rangeContainer">
            <div id="WTrange"></div>
        </div>

    </div>
</div>

NOTICE: Now there is HTML for the jQuery UI slider on the same page


ESSENTIAL QUESTION: Because the HTML contents of <div class="SliderContainer"> weren't part of the DOM on $(document).ready() their events have to be .delegate()d up the DOM tree. How can I delegate this jQuery UI slider so my JS recognizes it?


Solution

  • Please review: https://api.jqueryui.com/slider/

    Here is an example of how to use this.

    $(function() {
      $('#WTrange').slider({
        range: true,
        values: [2.50, 30.00],
        min: 2.50,
        max: 30.00,
        step: 0.01
      }).on("slidechange", function(event, ui) {
        alert('Things have changed!');
      }).on("slide", function(event, ui) {
        if ((ui.values - ui.values[1]) < 3) {
          return false;
        }
        $('#showWT').html(ui.values[0] + 'Ct - ' + ui.values[1] + 'Ct');
        $('#minWT').val(ui.values[0]);
        $('#maxWT').val(ui.values[1]);
      });
    });
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <div class="SliderContainer">
      <input type="hidden" id="minWT" value="2.5" />
      <input type="hidden" id="maxWT" value="30" />
      <p class="wtNumber" id="showWT">2.5Ct - 30Ct</p>
      <div class="rangeContainer">
        <div id="WTrange"></div>
      </div>
    </div>

    Update

    As you stated in your edit, you are using .load() to pull in the needed HTML and then initialize it with jQuery UI Slider. I would use the complete callback feature of .load(). See More: https://api.jquery.com/load/

    Type: Function( String responseText, String textStatus, jqXHR jqXHR ) A callback function that is executed when the request completes.

    $('button').on('click', function() {
      $('#pageContent').load('sliderPage.php', function(){
        $('#WTrange').slider({
          range: true,
          values: [2.50, 30.00],
          min: 2.50,
          max: 30.00,
          step: 0.01,
          slide: function(event, ui) {
            if ((ui.values - ui.values[1]) < 3) {
              return false;
            }
            $('#showWT').html(ui.values[0] + 'Ct - ' + ui.values[1] + 'Ct');
            $('#minWT').val(ui.values[0]);
            $('#maxWT').val(ui.values[1]);
          },
          change: function(event, ui) {
            alert('Things have changed!');
          }
        });
      });
    });
    

    Now the elements will exist and the events will bind successful. You won't need delegation. The crux of the issue would be that you cannot delegate the initialization of .slider(). You can delegate change and slide events, but not the initial .slider() call, since it's not an event.

    Update 2

    As your content cannot be tested by others, I created a pseudo test in JSFiddnle: https://jsfiddle.net/Twisty/zjeLdmw5/8/

    This echos the HTML back to the .load() function in the same sort of way your pages load the content. When I click the button, the content is replaced with the HTML and then Slider is initialized.

    This shows the code I suggested works. There may be some other issue with your code that is not yet clear.