Vue.js and jQuery Draggable not working - jquery-ui

I've the following code:
JS
new Vue({
el: 'body',
data: {
image: 'http://wpmedia.news.nationalpost.com/2016/07/faroe-islands-sheep-view.jpg?quality=65&strip=all&w=620'
},
ready:function(){
this.setDraggable();
},
methods:{
setDraggable: function(){
var x1 = $('.img').width();
var x2 = $('.bb-image-drag').width();
$( ".bb-image-drag" ).draggable({
axis: "x",
drag: function(event, ui) {
if(ui.position.left >= 0)
ui.position.left = 0;
else if(ui.position.left <= x1 - x2)
ui.position.left = x1 - x2;
},
stop: function(event, ui) { $('#bbOffset').val(ui.position.left); }
});
}
}
})
Markup
<div class="img mob-nom-img" style="position: relative;">
<div class="text-center">
<span style="margin-top: 400px;left: 30%;" class="drag-label">↔ Drag to adjust</span></div>
<img :src="image" class="bb-image-drag">
</div>
https://jsfiddle.net/yn99de5n/3/
But, the draggable is not functioning properly. It's flickring and dragging to left 0 to max quickly.
I think it's mainly because the Vue is rendered after the draggable function call. But I couldn't find a method to solve this. Any idea?

Related

Use clone helper only when button pressed

I am trying to make it so that my draggable element is only cloned when I press the Control button and drag it. My options for the draggable() function are:
var pressedKeys = {};
window.onkeyup = function(e) { pressedKeys[e.keyCode] = false; }
window.onkeydown = function(e) { pressedKeys[e.keyCode] = true; }
var draggable_options = {
snap: '.slot',
snapMode: 'inner',
scroll: false,
start: function(event,ui){
if (pressedKeys[17]){
$(ui.helper).draggable('option','helper','clone');
}
},
}
Is this even possible? I've tried ui.element and also this and neither have worked. I'm not sure if you can change options at runtime for the jquery functions.
Consider the following.
$(function() {
var pressedKeys = {
17: false
};
$(window).on({
keyup: function(e) {
pressedKeys[e.keyCode] = false;
$("#draggable").draggable("option", "helper", "original");
},
keydown: function(e) {
console.log("Key Pressed: " + e.keyCode);
pressedKeys[e.keyCode] = true;
$("#draggable").draggable("option", "helper", "clone");
}
})
$("#draggable").draggable({
snap: '.slot',
snapMode: 'inner',
scroll: false
});
});
#draggable {
width: 150px;
height: 150px;
padding: 0.5em;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<div id="draggable" class="ui-widget-content">
<p>Drag me around</p>
</div>
An alternative solution. I would advise this solution personally.
$(function() {
var pressedKeys = {
17: false
};
$(window).on({
keyup: function(e) {
pressedKeys[e.keyCode] = false;
},
keydown: function(e) {
console.log("Key Pressed: " + e.keyCode);
pressedKeys[e.keyCode] = true;
}
})
$("#draggable").draggable({
snap: '.slot',
snapMode: 'inner',
scroll: false,
helper: function() {
return (pressedKeys[17] ? $(this).clone().removeAttr("id") : $(this));
}
});
});
#draggable, .ui-draggable {
width: 150px;
height: 150px;
padding: 0.5em;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<div id="draggable" class="ui-widget-content">
<p>Drag me around</p>
</div>
See more: https://api.jqueryui.com/draggable/#option-helper
The start callback is triggered too late to generate a Clone. This is why helper option offers a Function to dynamically create the the helper as needed.
Here is the _createHelper code from the library:
var o = this.options,
helperIsFunction = typeof o.helper === "function",
helper = helperIsFunction ? $( o.helper.apply( this.element[ 0 ], [ event ] ) ) : ( o.helper === "clone" ? this.element.clone().removeAttr( "id" ) : this.element );
Simply perform a similar activity to replicate the code. Conditionally, it will return the original or a clone.

jquery-ui - range slider - save values on slider handles after input submit

I am trying to get the slider values to stay where they were moved to when the SEARCH button is pressed. They default back to the starting values whenever the search is pressed. I have tried all sorts of things and nothing appears to work. Any help would be appreciated.
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="css/jquery-ui.css">
</head>
<body>
<form method="post" id="formMain">
<label>Price Range</label>
<div>
<div id="slider-range" ></div>
<input type="hidden" name="price_l" id="price_l" value="<?php echo
(isset($_REQUEST["price_l"])?$_REQUEST["price_l"]:"50000")?>"/>
<input type="hidden" name="price_h" id="price_h" value="<?php echo
(isset($_REQUEST["price_h"])?$_REQUEST["price_h"]:"400000")?>"/>
<input type="text" name="text" id="amount" disabled="" />
</div>
<div>
<input type="submit" value="Search">
</div>
</form>
<script src="js/jquery-3.5.1.min.js"></script>
<script src="js/jquery-ui.js"></script>
<script>
var siteSliderRange = function() {
$( "#slider-range" ).slider({
range: true,
min: 5000,
max: 450000,
step: 5000,
values: [ 50000, 400000 ],
slide: function( event, ui ) {
$( "#amount" ).val( "$" + ui.values[ 0 ] + " - $" + ui.values[ 1 ] );
// when the slider values change, update the hidden fields
$("#price_l").val(ui.values[ 0 ]);
$("#price_h").val(ui.values[ 1 ]);
}
});
$( "#amount" ).val( "$" + $( "#slider-range" ).slider( "values", 0 ) +
" - $" + $( "#slider-range" ).slider( "values", 1 ) );
};
siteSliderRange();
</script>
</body>
</html>
Consider the following example that uses localStorage to store the slider values in the browser.
https://jsfiddle.net/Twisty/e28pqhy9/11/
HTML
<fieldset class="ui-widget">
<legend>Price Range</legend>
<div class="content">
<div id="slider-range"></div>
<div id="amount"></div>
<button>Search</button>
</div>
</fieldset>
JavaScript
$(function() {
function getValues(k) {
if (k == undefined) {
return false;
}
var v = localStorage.getItem(k);
if (v != null) {
return JSON.parse(v);
} else {
return -1;
}
}
function setValues(k, v) {
if (k == undefined || v == undefined) {
return false;
}
localStorage.setItem(k, JSON.stringify(v));
}
function showRange(tObj, v) {
tObj.html("$" + v[0] + " - $" + v[1]);
}
function searchRange(q) {
$.post("searchRange.php", {
price_l: q[0],
price_h: q[1]
}, function(response) {
// Do the needful
})
}
function siteSliderRange(tObj) {
tObj.slider({
range: true,
min: 5000,
max: 450000,
step: 5000,
values: [50000, 400000],
slide: function(event, ui) {
showRange($(this).next(), ui.values);
},
stop: function(e, ui) {
setValues($(this).attr("id"), ui.values);
}
});
}
function init() {
var cVal = getValues("slider-range");
if (cVal != -1) {
showRange($("#amount"), cVal);
siteSliderRange($("#slider-range"));
$("#slider-range").slider("values", cVal);
} else {
showRange($("#amount"), [50000, 400000]);
siteSliderRange($("#slider-range"));
setValues("slider-range", [50000, 400000]);
}
$(".content button").click(function() {
searchRange($("#slider-range").slider("values"));
});
}
init();
});
This will check the localStorage upon initialization to see if any values have been stored. If not, 50000 and 400000 are set as defaults. If there is a value, it will be loaded to both the Slider and the display area. Moving away from the Form model will give you added security. Less chance of someone entering their own values by manually enabling the Text field.
When the User moves the Slider, the display is updated. When they stop it then updates the localStorage. This ensure if they refresh the page or navigate back later, the Slider will recall their selection.
When the Search button is clicked, an AJAX Post is performed, this sends the data to PHP and expects some results to be passed back. I assume those results would be appended to the page.
Update
New Example for PHP: https://jsfiddle.net/Twisty/e28pqhy9/20/
If you want to echo the PHP Values, you can do this, you just need to adjust your JS to look for these values.
HTML
<form>
<fieldset class="ui-widget">
<legend>Price Range</legend>
<div class="content">
<div id="slider-range"></div>
<div class="amount-display"></div>
<input type="hidden" id="amount" value="<?php echo $price_l . ',' . $price_h ?>" />
<button type="submit">Search</button>
</div>
</fieldset>
</form>
JavaScript
$(function() {
function getValues() {
return $("#amount").val().split(",");
}
function setValues(v) {
$("#amount").val(v.join(","));
}
function showRange(tObj, v) {
tObj.html("$" + v[0] + " - $" + v[1]);
}
function siteSliderRange(tObj) {
tObj.slider({
range: true,
min: 5000,
max: 450000,
step: 5000,
values: getValues(),
slide: function(event, ui) {
showRange($(this).next(), ui.values);
},
stop: function(e, ui) {
setValues($(this).attr("id"), ui.values);
}
});
}
function init() {
var cVal = getValues();
showRange($(".amount-display"), cVal);
siteSliderRange($("#slider-range"));
}
init();
});
When the form is submitted, the $_POST['amount'] will be a string and you can use this to convert it back:
PHP
$amounts = explode(",", $_POST['amount']);
$price_l = (int)$amounts[0];
$price_h = (int)$amounts[1];

Jquery UI drag and drop - dragged item dissapears when dropped only on mobile

I am trying to get drag and drop working properly and on desktop of laptop pc it is fine. However, on a mobile device, when I drag and drop, when dropped, the dragged item dissapears underneath (i think) everything else and I really am unable to work out why.
I have uploaded a page showing the problem to http://mailandthings.co.uk/dam1/
I have tried setting the zindex in the draggable code and that makes no difference
var $dragContainer = $("div.drag-container");
var $dragItem = $("div.drag-item");
$dragItem.draggable({
cursor: "move",
snap: "div.drag-container",
snapMode: "inner",
snapTolerance: 10,
helper: "clone",
handle: "i",
zIndex: 10000
});
$dragContainer.droppable({
drop: function (event, ui) {
var $elem = $(event.toElement);
var obj = {
posX: event.pageX - $dragContainer.offset().left - event.offsetX,
posY: event.pageY - $dragContainer.offset().top - event.offsetY,
data: $elem.data(),
html: $elem.html()
};
addElement(obj);
masterPos.push(obj);
}
});
function addElement(obj) {
var $child = $("<div>");
$child.html("<i>" + obj.html + "</i>").addClass("drop-item drop-item-mobile");
$child.attr("data-type", obj.data.type);
$child.css({
top: obj.posY,
left: obj.posX
});
$dragContainer.append($child);
}
If it using jQuery UI Touch Punch 0.2.3
Does anyone have any ideas?
There was sort of a logistical issue that I found. Based on your code, I could identify the following state / logic:
User drags an item (A, B, C) to the car image to indicate a Dent, Scratch, or Heavy Damage
The Drop Point indicates where the Type of damage is located
When the dragged item is dropped, a new object should be created that indicates the Type and stores the location on the car map
This new object replaces the dragged item and is appended to the container
To expand on this, you have the following code that is the dragged element, for example:
<div class="drag-item ui-draggable" style="">
<i data-type="A" class="ui-draggable-handle">A</i>Dent
</div>
This is important when creating the new object. In your current code, you're requesting data from an object that does not have any data attributes, $elem.data(). Remember that this is the <div> that contains the <i> that has the attribute. So data is null or undefined. You will want to capture the data from the child element: $elem.find("i").data().
Also, since you append all the HTML to your new object, you make a double wrapped element. $child will look like:
<div class="drop-item drop-item-mobile">
<i>
<div class="drag-item ui-draggable" style="">
<i data-type="A" class="ui-draggable-handle">A</i>Dent
</div>
</i>
</div>
I do not think this was your intention. I suspect your intention was to create:
<div class="drop-item drop-item-mobile">
<i>A</i>
</div>
Here is an example of all this: https://jsfiddle.net/Twisty/g6ojp4ro/40/
JavaScript
$(function() {
var theForm = document.forms.form1;
if (!theForm) {
theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
var masterPos = [];
$("#hidpos").val('');
var $dragContainer = $("div.drag-container");
var $dragItem = $("div.drag-item");
$dragItem.draggable({
cursor: "move",
snap: "div.drag-container",
snapMode: "inner",
snapTolerance: 10,
helper: "clone",
handle: "i",
zIndex: 10000
});
$dragContainer.droppable({
drop: function(event, ui) {
var $elem = ui.helper;
var type = ui.helper.find("i").data("type");
var $child = $("<div>", {
class: "drop-item drop-item-mobile"
}).data("type", type);
$("<i>").html(type).appendTo($child);
$child.appendTo($dragContainer).position({
of: event
});
var obj = {
posX: $child.offset().top,
posY: $child.offset().left,
data: $child.data(),
html: $child.prop("outerHTML")
};
masterPos.push(obj);
}
});
$("map").imageMapResize();
// Save button click
$('#form1').submit(function(e) { //$("#btnsave").click(function () {
if (masterPos.length == 0) {
$("#spnintro").html("Oops!");
$("#spninfo").html("No position data was entered");
$("#dvinfo").fadeTo(5000, 500).slideUp(500, function() {});
} else {
$("#hidpos").val(JSON.stringify(masterPos));
$.ajax({
url: '/handlers/savepositions.ashx',
type: 'POST',
data: new FormData(this),
processData: false,
contentType: false,
success: function(data) {
$("#spnintro").html("Success!");
$("#spninfo").html("Position data has been saved");
$("#dvinfo").fadeTo(5000, 500).slideUp(500, function() {});
}
});
}
e.preventDefault();
});
});
Tested with Mobile client at: https://jsfiddle.net/Twisty/g6ojp4ro/40/show/ and is working as expected.
Hope that helps.

Change HTML when on start dragging with Jquery UI

I've been working on a drag and drop UI with jquery UI.
I have a bunch of elements like this, that are draggable:
<i class="fa fa-laptop fa-fw"></i> Server
I can drag and clone this onto a drop zone. But when I start dragging the original element, I want to change the HTML to be
<i class="fa fa-laptop fa-5"></i>
The current code looks like this, but should I be doing something in the start event handler to clone, and change the html of the cloned element?
function MakeDraggable(ele) {
ele.draggable({
grid: [20, 20 ],
//revert: "invalid",
helper: 'move'
});
}
$(function() {
// Make the .draggable class a Draggable
//
$( ".draggable" ).draggable({
grid: [ 20, 20 ],
helper: 'clone',
appendTo: '#dropzone-panel',
start: function(event, ui) {
console.log("Dragging me.....");
}
});
// Setup the dropzone
//
$("#dropzone-panel").droppable({
drop: function(event, ui) {
console.log("Drop Event Fired");
// Get the original element id
var id = ui.draggable.attr("id");
// If this element is a copy already
// then dont clone it.
//
if (id.indexOf("-copy-") >= 0) {
console.log("This is a copy");
} else {
// This is the orginal, so clone and create a new id
var number_of_clones = document.querySelectorAll('*[id^="'+id+"-copy-" +'"]').length;
console.log("found [" +number_of_clones +"] copies");
var pos = ui.position;
var $obj = ui.draggable.clone().attr("id", id+"-copy-"+number_of_clones);
$obj.css({
position: 'absolute',
top: pos.top + "px",
left: pos.left + "px"
});
$obj.appendTo("#dropzone-panel");
// Make the clone draggable
MakeDraggable($obj);
}
}
});

jQuery Slider, Combined Values - Having difficulty with Small Increments

I have designed a jQuery Slider Control that takes a 'pool' of points, and allows them to be distributed between multiple sliders. It works pretty well, except that I am having some problems with very small overflow increments.
Basically, it is possible to make adjustments in large quantities based on mouse movement, and so it lets someone 'spend' more in a slider than intended. I am at a loss as to how to deal with this. Posted below is my entire code.
To test it, build a simple HTML page with this code and try sliding the first two sliders all the way to 500, then try sliding the third. It won't slide (intended behavior).
Then, slide the first or second slider back a little bit (subtracting), and slide the third forward. You are able to occasionally go over the intended 'spendable' bounds.
Sample will require jQuery UI, latest version from google CDN.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.5/jquery-ui.js"></script>
Javascript
<style type="text/css">
#eq > div {
width:300px;
}
#eq > div .slider {
width: 200px; float: left;
}
#eq > div > span {
float: right;
}
</style>
<script type="text/javascript">
$(document).ready(function () {
var spendable = 1000;
var spent = 0;
function spend(quantity) {
spent += quantity;
$('#attemptedToSpend').text(spent);
}
function change(previous, current) {
var adjustment = (current - previous);
$('#change').text(adjustment);
return adjustment;
}
function calculateSpent() {
var totalled = 0;
$("#eq .slider").each(function () {
totalled += parseInt($(this).parent('div:eq(0)').find('.spent').text());
});
$('#spent').text(totalled);
}
$("#eq .slider").each(function () {
var current = 0;
var previous = 0;
var adjustment = 0;
$(this).slider({
range: "min",
value: 0,
min: 0,
max: 500,
step: 1,
animate: true,
orientation: "horizontal",
start: function (event, ui) {
},
stop: function (event, ui) {
},
slide: function (event, ui) {
// set the current value to whatever is selected.
current = ui.value;
// determine the adjustment being made relative to the last
// adjustment, instead of just the slider's value.
adjustment = change(previous, current);
if (spent >= spendable) {
if (adjustment > 0)
return false;
}
// spend the points, if we are able to.
spend(adjustment);
// set the previous value
previous = current;
$(this).parent('div:eq(0)').find('.spent').text(current);
calculateSpent();
}
});
});
});
</script>
Html
<p class="ui-state-default ui-corner-all" style="padding: 4px; margin-top: 4em;">
<span style="float: left; margin: -2px 5px 0 0;"></span>Distribution
</p>
<strong>Total Points Spendable: </strong><div id="spendable">1000</div>
<strong>Total Points Spent (Attempted): </strong><div id="attemptedToSpend">0</div>
<strong>Total Points Spent: </strong><div id="spent">0</div>
<strong>Change: </strong><div id="change">0</div>
<div id="status"></div>
<div id="eq">
<div style="margin: 15px;" id="test1">Test1</div>
<br />
<div class="slider"></div><span class="spent">0</span>
<div style="margin: 15px;" id="test2">Test2</div>
<br />
<div class="slider"></div><span class="spent">0</span>
<div style="margin: 15px;" id="test3">Test3</div>
<br />
<div class="slider"></div><span class="spent">0</span>
<div style="margin: 15px;" id="test4">Test4</div>
<br />
<div class="slider"></div><span class="spent">0</span>
<div style="margin: 15px;" id="test5">Test5</div>
<br />
<div class="slider"></div><span class="spent">0</span>
</div>
I tried keeping your script intact, but ended up largely rewriting it. It should be solid now. Good news: I have only changed the JS, everything else is intact, though I don't update all your monitoring fields any more.
DEMO
$(
function()
{
var
maxValueSlider = 500,
maxValueTotal = 1000,
$sliders = $("#eq .slider"),
valueSliders = [],
$displaySpentTotal = $('#spent');
function arraySum(arr)
{
var sum = 0, i;
for(i in arr) sum += arr[i];
return sum;
}
$sliders
.each(
function(i, slider)
{
var
$slider = $(slider),
$spent = $slider.next('.spent');
valueSliders[i] = 0;
$slider
.slider(
{
range: 'min',
value: 0,
min: 0,
max: maxValueSlider,
step: 1,
animate: true,
orientation: "horizontal",
slide:
function(event, ui)
{
var
sumRemainder = arraySum(valueSliders) - valueSliders[i],
adjustedValue = Math.min(maxValueTotal - sumRemainder, ui.value);
valueSliders[i] = adjustedValue;
// display the current total
$displaySpentTotal.text(sumRemainder + adjustedValue);
// display the current value
$spent.text(adjustedValue);
// set slider to adjusted value
$slider.slider('value', adjustedValue);
// stop sliding (return false) if value actually required adjustment
return adjustedValue == ui.value;
}
}
);
}
);
}
);

Resources