Develop a segmented control-style component with a draggable feature

In my project, I have set up 2 divs that are positioned alongside each other, with a background div referred to as the "bg div". Upon selecting one of the divs, the bg div transitions on top of the selected one, creating a segmented controller effect.

Now, my next objective is to make the bg div draggable. If it is dragged but not all the way to either side, it should snap to the side where it is mostly positioned.

I am aiming for functionality similar to this: https://i.sstatic.net/XBhjK.gif

However, when I tried to implement the draggable feature (using JQuery UI) and set z-index to -1, the div stopped being draggable. It also didn't snap to the sides efficiently unless dragged completely. Additionally, there was an odd delay in dragging due to the transition effect.

Issues:

  • How can I enable draggability with z-index set to -1?
  • How can I ensure the div snaps accurately to the dominant side?
  • How can I avoid the weird dragging behavior caused by the transition?

Links to demos: JSFiddle without issues JSFiddle with issues

$('#bckgrnd').draggable({
    axis: "x",
    containment: "parent",
    snap: ".labels"
});
#radios {
    position: relative;
    width: 370px;
}
input {
    display: none;
}
#bckgrnd,
#bckgrndClear,
.labels {
    width: 50%;
    height: 30px;
    text-align: center;
    display: inline-block;
    float: left;
    padding-top: 10px;
    cursor: pointer;
}
.labels {
    outline: 1px solid green;
}
#bckgrnd {
    background-color: orange;
    position: absolute;
    left: 0;
    top: 0;
    transition: left linear 0.3s;
}
#rad1:checked ~ #bckgrnd {
    left: 0;
}
#rad2:checked ~ #bckgrnd {
    left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="radios">
  <input id="rad1" type="radio" name="radioBtn" checked>
  <label class="labels" for="rad1">First Option</label>
  <input id="rad2" type="radio" name="radioBtn">
  <label class="labels" for="rad2">Second Option</label>
  <div id="bckgrnd"></div>
</div>

Answer №1

Here is a solution that might help: http://jsfiddle.net/6j0538cr/ (Give it a try!)

Is there a way to make it draggable with an index of -1?

  • To achieve this, you can add two elements - one for the label with 'pointer-events:none;' to ignore mouse events and 'z-index:3', and another for the button with 'z-index:1'. This setup allows the label to float above all elements with z-index:3 while still making the background draggable.

How can I make it snap to the side where the bg div is mostly positioned?

  • You can easily calculate this by using the 'offset' and 'width' functions as shown below:

    // Calculating the middle of the 'background'
    var backgroundX = $('#bckgrnd').offset().left;
    var backgroundWidth = $('#bckgrnd').outerWidth();
    var backgroundMiddle = backgroundX + (backgroundWidth/2);
    
    // Calculating the middle of the radios on the page
    var radiosX = $('#radios').offset().left;
    var radiosWidth = $('#radios').outerWidth();
    var radiosMiddle = radiosX + (radiosWidth/2);
    
    // Compare the two to determine which side is closer
    if(radiosMiddle > backgroundMiddle){
        // Closer to the left
    }else{
       // Closer to the right
    }
    

Can it transition smoothly without any glitches?

  • You can utilize jQuery's 'animate' function instead of mixing CSS and JS animations to achieve a smooth transition effect.

Answer №2

To make an element draggable with index -1, a container div can be used to trap mouse events (perhaps for radios). When the mouse is clicked, the x value is recorded. During mouse movement (while the button is held down), the "delta" between the current mouse x and the initial x value is calculated and added to the original x position of the dragged element.

For snapping behavior, the min/max functions are utilized to restrict the delta. It seems like there may also be some animation involved in the snapping process. If the mouse is released within a specific range at either end, an animation is triggered to snap the element to one side or the other.

In addition, clicking within certain boundaries close to the edge will also trigger the snapping animation.

Answer №3

I could have potentially achieved this using radio elements within an <input> tag...
However, upon submission, the value of data-* can be sent.

Here is my approach:

https://i.sstatic.net/yyq7d.png

$(".io-toggler").each(function(){
  
  
  var io = $(this).data("io"),
      $opts = $(this).find(".io-options"),
      $clon = $opts.clone(),
      $span = $clon.find("span"),
      width = $opts.width()/2;
  
  $(this).append($clon);
  
  function swap(x) {
    $clon.stop().animate({left:  x}, 150);
    $span.stop().animate({left: -x}, 150);
    $(this).data("io", x===0 ? 0 : 1);
  }
  
  $clon.draggable({
    axis:"x",
    containment:"parent",
    drag:function(evt, ui){
      $span.css({left: -ui.position.left});
    },
    stop:function(evt, ui){
      swap( ui.position.left < width/2 ? 0 : width );
    }
  });
  
  $opts.on("click", function(){
    swap( $clon.position().left>0 ? 0 : width );
  });
  
  // Read and set initial predefined data-io
  if(!!io)$opts.trigger("click");
  // on submit read $(".io-toggler").data("io") value
  
});
.io-toggler{
  cursor:pointer;
  display:inline-block;
  position:relative;
  font:20px/1.5 sans-serif;
  background:url(https://i.sstatic.net/XVCAQ.png);
  color:#fff;
  border:4px solid transparent;
  border-radius: 50px;
  user-select:none;
  -webkit-user-select:none;
  -webkit-touch-callout:none;
}
.io-options{
  border-radius:50px;
  top:0;
  left:0;
  overflow:hidden;
}
.io-options span{
  position:relative;
  text-align:center;
  display:inline-block;
  padding: 3px 35px;
}

/* the jQ clone */
.io-options + .io-options{
  position:absolute;
  background:#fff;
  width:50%;
  height:100%;
  white-space:nowrap;
}
.io-options + .io-options span{
  color:#006cff;
}
<link href="https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css" rel="stylesheet" type="text/css" />
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>

<span class="io-toggler" data-io="0">
  <span class="io-options">
    <span>Thing1</span>
    <span>Thing2</span>
  </span>
</span>

<br><br>

<span class="io-toggler" data-io="1">
  <span class="io-options">
    <span>Yes</span>
    <span>No</span>
  </span>
</span>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The fabulous four of web development: Ajax, PHP, SQL, as well

I have encountered a problem that has left me stumped and unable to find a solution. Although I don't usually ask questions here, any help would be greatly appreciated. Here is the PHP code that handles the Ajax call: <?php session_start(); ...

What is the best way to limit the range slider before it reaches its maximum point?

I am currently utilizing angularjs to stop a range slider at 75%, however, the method I am using is not very efficient and is not working as desired. Is there anyone who can provide guidance on how to achieve this? Please note: I want to display a total ...

Tips for sending multiple pieces of data to an Arduino with NodeJS's serialport library

I am having trouble writing multiple data using the same port, but I see that node-serialport allows for reading multiple data functions simultaneously. How can I also write multiple data at the same time? This is how I have attempted it: - index.js- con ...

Running Docker does not provide a means to access the application

I am currently developing an Express (nodejs) application that is running on port 3000, showcasing a simple hello world, and is hosted on a public github repository. So far, everything is running smoothly and the code itself looks like this: var express ...

If the user inputs any text, Jquery will respond accordingly; otherwise,

I have a text field that is populated from a database, but if a user decides to change the value, I need to use that new value in a calculation. $('#ecost input.ecost').keyup(function(){ if (!isNaN(this.value) && this.value.length != ...

The checkbox remains unchecked after clicking on the button

I am attempting to add checkboxes before the list items in a listview on my page. When I click on the "edit" button, the listview successfully adds the checkboxes before the list items. However, when I try to click on the checkboxes, the page automatical ...

Utilizing External Library Functions with VueJS

I have been looking everywhere for a working example of how to connect an external JS file and call its function, but I haven't had any luck. Essentially, my goal is to link an external JavaScript file and execute its function using VueJS. <script ...

What status code should be used to redirect a request from one valid endpoint to another valid endpoint?

In my system, there are two main components: projects and proposals. Each proposal can exist in one of three states - pending, approved, or rejected. A project is always associated with a previously approved proposal. The API endpoint for proposals (/pro ...

Issues with Bootstrap's center-block functionality

I'm having trouble centering the navigation in my WordPress template that uses Bootstrap. I've tried using the center-block class on different elements, but the menu items are still aligning to the left. I even created a custom class in my custom ...

Why does my AJAX to PHP return an object result?

Here is the jQuery code I am using: $.ajax({ url: 'function.php', type: 'post', datatype: 'json', success: function(data){ var toAppend = &ap ...

Are there any additional dependencies required for Orbitdb to function properly?

When setting up Orbitdb to work with IPFS, it seems that there may be additional dependencies required that are not explicitly stated anywhere. I attempted to create a basic key-value database following the documentation: orbit.js const IPFS = require(&qu ...

The Bug in Microsoft Edge Blocking Scope Functionality

While this code functions flawlessly in Chrome and Firefox, it encounters difficulties performing in Edge: HTML: <ul> <li class="listItems">one</li> <li class="listItems">two</li> <li class="listItems">thre ...

Refresh Vue/Nuxt Components Fully

Understanding how this.$forceUpdate() functions, I am not simply looking to re-render the component. In Nuxt applications, page components have asyncData() as a lifecycle method that runs before created(). I utilize this method to retrieve initial data an ...

"The NextJS FetchError that occurred was due to a timeout issue (ET

After successfully deploying my project on CentOS 7, I set up port forwarding to access it through port 8080. This means that in order to use the site, you had to navigate to it using the IP followed by :8080. Below is the NGINX configuration I utilized fo ...

Using Nuxt.js to import custom NPM packages on a global scale

The installation process for Nuxt's plugins/modules system can be quite intricate. Despite attempting to follow various suggestions, I have struggled to accomplish a seemingly simple task. After installing the NPM package csv-parse (which can be found ...

What is the best way to pass a variable from one PHP file to another?

This question may seem like a duplicate, but I found no helpful answers elsewhere. I'm in the process of creating a website where users can respond to various types of questions. To achieve this, I've written code that displays the corresponding ...

"Picture positioned on the edge of the container, just on one

Using Bootstrap 4, I was able to create a container with a div positioned outside of the container on the right side. Now I am wondering if it is possible to achieve the same effect with a picture div. I managed to do so by using a pseudo-element :after an ...

The Jquery function for setting the active navigation tab based on the URL is currently over-selecting and choosing more options

I have a customized Jquery function that assigns the 'active' class to my navigation anchors based on the URL. While this function generally works, the issue arises when tab 1 is active - tabs 10, 11, and 12 also get marked as active. The code s ...

Preserve the scrollbar location when the page is refreshed

Is there a way to ensure that when the page is refreshed, the table content returns to its previous position rather than going back to the top? For example, if a user scrolls down and then triggers a page reload with a button, I would like the table conten ...

Using both withNextIntl and withPlaiceholder simultaneously in a NextJS project causes compatibility issues

I recently upgraded to NextJS 14 and encountered an issue when deploying my project on Vercel. The next.config.mjs file in which I wrapped my nextConfig in two plugins seemed to prevent the build from completing successfully. As a workaround, I decided t ...