JQuery: z-index ordering with draggable divs

為{幸葍}努か 提交于 2019-12-13 16:17:53

问题


In the following code I have:

  1. divs that pop up when you click the corresponding buttons
  2. if you drag them around, the (last) active object should be on top

Currently my code doesn't work as expected where the last active element is bought to the front. I am trying to create an extensible solution where there can be about five or so more buttons later with different content.

Also, is there a way to let the divs pop up near to the buttons that they are shown from? If I drag the button before I clicked it, the distance is a bit big.

My current attempt looks like this:

$(function() {
	/* Combine on ready logic into one place */
	$("#button-one").draggable({
		stack: 'div',
		containment: "body"
	});
	$("#content-one").draggable({
		stack: 'div',
		containment: "body"
	});
	/* Hide all trip elements except for first */
	$('.trip', '#content-one').not(':first').hide();
});
$('#button-one').on('mouseup', function() {
	if (!$(this).hasClass('ui-draggable-dragging')) {
		$("#content-one").toggle();
	}
});
$('#content-one').on('mouseup', function() {
	/* Reuse same logic in #button mouseup handler */
	if (!$(this).hasClass('ui-draggable-dragging')) {
		/* 
		If content element if not dragging, treat mouse up as conclusion
		of click event and rotate visibility of trip elements like this
		*/
		let trip = $('.trip:visible', '#content-one');
		let next = trip.next().length === 0 ? $('.trip:first', '#content-one') : trip.next();
		trip.hide();
		next.show();
	}
});
$(function() {
	$("#button-two").draggable({
		stack: 'div',
		containment: "body"
	});
});
$('#button-two').on('mouseup', function() {
	if (!$(this).hasClass('ui-draggable-dragging')) {
		// your click function
		$("#content-two").toggle();
	}
});
$(function() {
	$("#content-two").draggable({
		stack: 'div',
		containment: "body"
	});
});
body,
html {
position: absolute;
padding: 0;
margin: 0;
width: 100%;
overflow-x: hidden;
overflow-y: inherit;
cursor: default;
}

#content {
max-width: 100vw;
height: 150vh;
}

#one {
left: 5%;
top: 5%;
position: absolute;
}

#button-one {
width: 100px;
height: 100px;
background-color: cyan;
position: absolute;
}

#content-one {
display: none;
cursor: all-scroll;
top: 10%;
left: 10%;
position: absolute;
top: 20px;
left: 20px;
}

.trip {
width: 200px;
height: 200px;
background-color: blue;
color: white;
}

#two {
left: 15%;
top: 15%;
position: absolute;
}

#button-two {
width: 100px;
height: 100px;
background-color: darkgrey;
position: absolute;
}

#content-two {
display: none;
cursor: all-scroll;
position: absolute;
top: 20px;
left: 20px;
width: 200px;
height: 200px;
background-color: green;
color: white;
}
<div id="content">

<div id="one">
<div id="button-one">Button 1</div>

<div id="content-one">
<div class="trip">div 1</div>
<div class="trip">div 2</div>
<div class="trip">div 3</div>
</div>
</div>

<div id="two">
<div id="button-two">Button 2</div>
<div id="content-two">Hey</div>
</div>

</div>

<script src="https://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="https://code.jquery.com/ui/1.8.21/jquery-ui.min.js"></script>
<script src="https://raw.githubusercontent.com/furf/jquery-ui-touch-punch/master/jquery.ui.touch-punch.min.js"></script>

Thank you for your help! :)


回答1:


Based on this answer of mine to your previous question - with some modifications I'd do it like:

  • Use data-* attribute to store the target content selector ID
  • Use Event.clientX / Y - to get the click coordinates
  • Use .css({left: X, top: Y}) to place your content
  • Place all your elements inside the same parent to account for the problematic z-indexing and help stack: '.draggable', do its job

jQuery($ => {

  const $drag = $('.draggable').draggable({
    stack: '.draggable',
    containment: 'body'
  });

  // Fix out-of-containment glitch
  $($drag.draggable('option').containment).on('mouseleave', () => $drag.trigger('mouseup')); 
  
  let z = 10; // Contents zIndex (will increment on content toggle)

  $('.button').on('click', function(ev) {
    if ($(this).hasClass('ui-draggable-dragging')) return;
    $($(this).data('content')).css({left: ev.clientX, top: ev.clientY, zIndex: ++z}).toggle();
  });

  $('.content').on('click', function() {
    const $trip = $(this).find('.trip'), tripL = $trip.length;
    this._count |= 0;
    $trip.eq(++this._count % tripL).show().siblings($trip).hide();
  });

});
html,
body {
  height: 100%;
  margin: 0;
}

body {
  background-color: grey;
}

.button {
  width: 30vh;
  height: 30vh;
  background-color: cyan;
}

.content {
  width: 45vh;
  height: 45vh;
  display: none;
  cursor: all-scroll;
  position: absolute;
  background-color: blue;
  color: white;
}

.trip~.trip {
  display: none;
}

[data-content="#content-two"] {background: aquamarine;}
#content-two {background: fuchsia;}
<div class="button draggable" data-content="#content-one">Toggle one</div>
<div class="content draggable" id="content-one">
  <div class="trip">ONE 1</div>
  <div class="trip">ONE 2</div>
  <div class="trip">ONE 3</div>
</div>

<div class="button draggable" data-content="#content-two">Toggle two</div>
<div class="content draggable" id="content-two">
  <div class="trip">TWO 1</div>
  <div class="trip">TWO 2</div>
</div>


<script src="https://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="https://code.jquery.com/ui/1.8.21/jquery-ui.min.js"></script>
<script src="https://raw.githubusercontent.com/furf/jquery-ui-touch-punch/master/jquery.ui.touch-punch.min.js"></script>



回答2:


To achieve the order behaviour you're after, I would suggest some changes to your HTML structure. What you want to aim for is a "flat" HTML structure so that draggable elements (ie those that are currently nested in #one and #two) can be z-ordered relative to one another (ie in the same containing element/DIV).

With that done, you could then use the start event hook on draggable() to set element that's "currently on top". A simple way to do that would be to introduce a CSS class that sets the z-index property.

To position the draggable near the button being clicked, you can use JQuerys position() method to get the top/left coordinates of the button being clicked. You can then pass those directly to the css() method of the draggable element like this:

  /* Position of this button */
  const position = $(buttonElement).position();

  /* Position target near the button with slight offset */
  draggableElement.css({
    left : position.left + 10,
    top : position.top + 10
  });  

The following revisions to your code should do the trick:

/* 
  Helper function assigns the element as "top most" element
  relative to other elements
  */
function setToTop(element) {

  var zIndexMax = 0;

  $('.content, .button').each(function(i, elem) {

    var zIndex = Number.parseInt($(elem).css('z-index'));
    
    zIndex = Number.isNaN(zIndex) ? 0 : zIndex;

    zIndexMax = Math.max(zIndexMax, zIndex);     
  });
  
  element.css({
    zIndex: zIndexMax + 1
  });
}

$(function() {

  /* Prevent drag from continuing after mouse leaves document */
  $(document).mouseleave(function() {
    $(document).trigger("mouseup")
  });

  $(".content, .button").draggable({
    helper: "original",
    containment: "body",
    start: function(event, ui) {

      /* 
      ui.helper is the element that we're starting to drag 
      */
      setToTop(ui.helper);
    }
  });

  $('.trip').not(':first').hide();
});

$('.button').on('mouseup', function() {

  /* Cause clicked button to come to front */
  setToTop($(this));

  if (!$(this).hasClass('ui-draggable-dragging')) {

    var toggleTarget = $(".content" + $(this).data("target"));

    toggleTarget.toggle();

    if (toggleTarget.is(':visible')) {

      /* Cause newly toggled element to be visible on top */
      setToTop(toggleTarget);

      /* Position of this button */
      const position = $(this).position();

      /* Position target near the button with slight offset */
      toggleTarget.css({
        left: position.left + 10,
        top: position.top + 10
      });
    }
  }
});

$('.content').on('mouseup', function() {

  if (!$(this).hasClass('ui-draggable-dragging')) {

    let trip = $('.trip:visible', this);
    let next = trip.next().length === 0 ? $('.trip:first', this) : trip.next();
    trip.hide();
    next.show();
  }
});
body,
html {
  position: absolute;
  padding: 0;
  margin: 0;
  width: 100%;
  overflow-x: hidden;
  overflow-y: inherit;
  cursor: default;
}

#content {
  max-width: 100vw;
  height: 150vh;
}

.content {
  display: none;
  cursor: all-scroll;
  position: absolute;
  top: 20px;
  left: 20px;
  width: 200px;
  height: 200px;
  background-color: green;
  color: white;
}

.content.one {
  top: 10%;
  left: 10%;
}

.content.two {
  background-color: green;
  color: white;
}

.trip {
  width: 200px;
  height: 200px;
  background-color: blue;
  color: white;
}

.button {
  width: 100px;
  height: 100px;
  position: absolute;
}

#two {
  left: 15%;
  top: 15%;
  background-color: darkgrey;
}

#one {
  left: 5%;
  top: 5%;
  background-color: cyan;
}
<div id="content">

  <div id="one" class="button" data-target=".one">Button 1</div>
  <div id="two" class="button" data-target=".two">Button 2</div>

  <div class="content two">Hey</div>

  <div class="content one">
    <div class="trip">div 1</div>
    <div class="trip">div 2</div>
    <div class="trip">div 3</div>
  </div>

</div>

<script src="https://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="https://code.jquery.com/ui/1.8.21/jquery-ui.min.js"></script>
<script src="https://raw.githubusercontent.com/furf/jquery-ui-touch-punch/master/jquery.ui.touch-punch.min.js"></script>

Update

To prevent glitchcy drag behaviour continuing after the mouse leaves the document during a drag you can do this:

/* 
  Helper function assigns the element as "top most" element
  relative to other elements
*/
function setToTop(element) {

  var zIndexMax = 0;

  $('.content, .button').each(function(i, elem) {

    var zIndex = Number.parseInt($(elem).css('z-index'));        
    zIndex = Number.isNaN(zIndex) ? 0 : zIndex;
    zIndexMax = Math.max(zIndexMax, zIndex);     
  });

  element.css({
    zIndex: zIndexMax + 1
  });
}


来源:https://stackoverflow.com/questions/57351370/jquery-z-index-ordering-with-draggable-divs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!