JQuery Sortable get Target item - javascript

I have a table with the following structure:
<tbody>
<tr>
<td>
<button id=“elem_id”>Move</button>
</td>
<td>
</td>
</tr>
With this script:
$('tbody').sortable({
handle: '.drag_handle',
group: 'nested',
animation: 600,
ghostClass: 'drag_drop_class',
fallbackOnBody: true,
swapThreshold: 0.65,
onEnd: function (evt) {
var btn = evt.item.firstElementChild.firstElementChild;
let id = btn.id;
let oldIndex = evt.oldIndex;
let newIndex = evt.newIndex;
//TODO: Em vez do index da linha tenho de obter o id do destino
let component_name = btn.getAttribute('data-component');
// alert("[" + component_name + "] Alterou id=" + id + " da pos[" + oldIndex + "] para a pos[" + newIndex +"]");
//TODO: Emitir um evento para o componente livewire da tabela de modo a atualizar
Livewire.emitTo(component_name, 'reorder', id, newIndex);
}
});
I have access to the element/item that was dragged, but now I need the element/item that was dropped in to, I've tried a lot of stuff but nothing worked.
I have access to the index but that won't help me because the Data Table I'm using uses pagination and I need to get the position (its a property) of the item by id.
The project I'm working on uses this scripts:
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.10.2/Sortable.min.js" ></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-sortablejs#latest/jquery-sortable.js"></script>

Maybe this example will help you. I could found an onEnd function within Sortable Jquery Ui.
HTML
<ul id="sortable">
<li class="name1">Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
JQuery
$( "#sortable" ).sortable({
update: function( event, ui ) {
var item = ui.item;
var target_prev = ui.item.prev();
var target_next = ui.item.next();
console.log('item', item);
console.log('target_prev', target_prev);
console.log('target_next', target_next);
}
});
Just watch the console.log at your Developer Tools.

I figured out the project I'm working on does not use JQuery UI. It uses SortableJS. The equivalent code to JQuery UI I found, was the following:
onEnd: function (evt) {
var current = evt.item;
var prev = evt.item.previousElementSibling;
var next = evt.item.nextElementSibling;
}
For anyone with the same problem just console.log() this vars.

Related

List of items in a column tabluator

I want a list of items in the tabulator in such a way that the sub-items are visible on clicking on that item. There is an onclick function for the column, but can I have on click for each element of the cell?
I want the column to look as follows:
Item 1
sub item 1
sub item 2
Item 2
sub item 3
sub item 4
The sub-items should be visible on clicking the corresponding Item
Using vanilla javascript and css, you can add one event listener to the document that checks if the clicked element is a li descended from the intended ul if so check for a child ul and toggle a class that will hide it like so:
document.addEventListener('click', function(event) {
if (event.target.matches('.collapsable-subs li')) {
let subList = event.target.querySelector('ul');
if (subList) {
subList.classList.toggle('collapsed')
}
}
}, false);
.collapsed {
display: none;
}
<ul class="collapsable-subs">
<li>Item 1
<ul class="collapsed">
<li>sub item 1</li>
<li>sub item 2</li>
</ul>
</li>
<li>Item 2
<ul class="collapsed">
<li>sub item 3</li>
<li>sub item 4</li>
<li>sub item 5 with subs
<ul class="collapsed">
<li>sub item 1</li>
<li>sub item 2</li>
</ul>
</li>
</ul>
</li>
</ul>
This allows for nesting many levels deep and only affects ul descendants of .collapsable-subs in case you have other nested uls in the page. You can also have multiple ul.collapsable-subs that will all have the same behavior with no additional scripting and no need for onclick attributes.
Tabulator comes with built in functionality to handle nested data.
You have the option of Nested Data Trees
or Nested Tables
You could also use Custom Row Formatters to manipulate the rows in any way you see fit
Define the columns with cellClick and append child elements. Take a look at below sample column definition.
{
title: "Select",
field: "select",
width: 90,
cellClick: function(e, cell) {
//cell is the DOM element which is clicked
//..write logic to append more elements to expand view
}
}
You do not need to copy event method everywhere
function identify() {
console.log("Element:\n" +
event.srcElement.outerHTML +
"\nRow:" +
event.srcElement.parentElement.outerHTML)
}
function identify2() {
var me = event.srcElement, me2 = me;
while((me2 = me2.parentElement) && me2.tagName != "LI");
console.log("Element:\n" +
me.outerHTML);
if(me2) console.log("Parent:\n" + me2.outerHTML);
else console.log("Parent LI tag not found.");
}
<table onclick="identify()">
<tr>
<td>00</td>
<td>01</td>
</tr>
<tr>
<td>10</td>
<td>11</td>
</tr>
</table>
<br/>
<ul onclick="identify2()">
<li>Coffee</li>
<li>Tea
<ul>
<li>Black tea</li>
<li>Green tea</li>
</ul>
</li>
<li>Milk</li>
</ul>
Tabulator.prototype.extendModule("format", "formatters", {
bold: function (cell, formatterParams) {
return "<strong>" + cell.getValue() + "</strong>"; //make the contents of the cell bold
},
uppercase: function (cell, formatterParams) {
return cell.getValue().toUpperCase(); //make the contents of the cell uppercase
},
makelist: function (cell, formatterParams) {
var des = cell.getValue()
var table = ""
des = des.split('/')
table += '<ul>'
for (a = 0; a < des.length; a++) {
recs = des[a].split(':')
recs[0] = recs[0].replace(' ','')
table += '<li>' + recs[0]
comps = recs[1].split('+')
if (comps[0] == '') {
break;
}
table += '<ul>'
for (b = 0; b < comps.length; b++) {
table += '<li class="comp" hide="no">' + comps[b] + '</li>'
}
table += '</ul></li>'
}
table += '</ul>'
return table;
}
});
$('#table').on("click",'.comp',function(e){
if ($(this).attr("hide") == "no"){
var element = $(this)
var name = $(this).text()
var parent_name = $(this).parent().parent().text().split(' ')[0]
$.ajax({
url:'/get_sub_items',
method:'POST',
data: JSON.stringify({name:name, parent_name:parent_name}),
success: function(response){
console.log(response)
list = '<ul>'
for(j = 0 ; j < response.length; j++){
list += '<li> '+response[j]+'</li>'
}
list += '</ul>'
$(list).appendTo(element)
}
})
$(this).attr("hide", "yes")
}
else{
$(this).children().hide()
$(this).attr("hide", "no")
}
e.preventDefault();
})

jQuery UI Sortable Connected Lists - Get Changed Status

I'm implementing sortable plugin of jQuery UI.
There are two columns and we can drag and drop an element from one column to another column.
I have the following javascript code:
$(function () {
$("#sortable1, #sortable2").sortable({
connectWith: ".connectedSortable",
update: function () {
var order1 = $('#sortable1').sortable('toArray').toString();
var order2 = $('#sortable2').sortable('toArray').toString();
alert("Order 1:" + order1 + "\n Order 2:" + order2);
$.ajax({
type: "POST",
url: "/echo/json/",
data: "order1=" + order1 + "&order2=" + order2,
dataType: "json",
success: function (data) {
}
});
}
}).disableSelection();
});
And HTML:
<ul id="sortable1" class="connectedSortable">
<li class="ui-state-default" id='item1'>Item 1</li>
<li class="ui-state-default" id='item2'>Item 2</li>
<li class="ui-state-default" id='item3'>Item 3</li>
<li class="ui-state-default" id='item4'>Item 4</li>
<li class="ui-state-default" id='item5'>Item 5</li>
</ul>
<ul id="sortable2" class="connectedSortable">
<li class="ui-state-highlight" id='item6'>Item 6</li>
<li class="ui-state-highlight" id='item7'>Item 7</li>
<li class="ui-state-highlight" id='item8'>Item 8</li>
<li class="ui-state-highlight" id='item9'>Item 9</li>
<li class="ui-state-highlight" id='item10'>Item 10</li>
</ul>
From above javascript, I can get the order of column 1 and column 2 after change as a whole. However, I would like to know the individual item that has been changed.
Like, in this image below, I have Item3 dragged from Column 1 to Column 2. I want to get output like - Item3_before=Column1,Item3_after=Column2.
In the javascript code above, by using start event like the update event, we can get the Before status of the elements, but not the individual element; it gives before status of all elements.
Few steps to take. I setup an Object to store the values at Activation, Pre-Sorting, and Post-Sorting for both lists. Once that was done, I used this method to determine the difference: JavaScript array difference
Working Example: https://jsfiddle.net/Twisty/kfedekmj/
jQuery
$(function() {
var items = {
"act": {
1: [],
2: []
},
"pre": {
1: [],
2: []
},
"post": {
1: [],
2: []
}
}
function log(et) {
if (!et) {
console.log("Activation");
console.log(" - order1: ", items.act[1].toString());
console.log(" - order2: ", items.act[2].toString());
}
if (et == "sortstart") {
console.log("Pre-Sort");
console.log(" - order1: ", items.pre[1].toString());
console.log(" - order2: ", items.pre[2].toString());
}
if (et == "sortupdate") {
console.log("Post-Sort");
console.log(" - order1: ", items.post[1].toString());
console.log(" - order2: ", items.post[2].toString());
}
}
function determineChange(a1, a2) {
var a = {},
diff = [],
i = 0;
for (i = 0; i < a1.length; i++) {
a[a1[i]] = true;
}
for (i = 0; i < a2.length; i++) {
if (a[a2[i]]) {
delete a[a2[i]];
} else {
a[a2[i]] = true;
}
}
$.each(a, function(k, v) {
diff.push(k);
});
return diff[0];
}
$("#sortable1, #sortable2").sortable({
connectWith: ".connectedSortable",
start: function(e, ui) {
// Start of Sort Order
items.pre[1] = $('#sortable1').sortable('toArray')
items.pre[2] = $('#sortable2').sortable('toArray')
log(e.type);
},
update: function(e, ui) {
// End of Sort Order
items.post[1] = $('#sortable1').sortable('toArray');
items.post[2] = $('#sortable2').sortable('toArray');
log(e.type);
/*
$.ajax({
type: "POST",
url: "/echo/json/",
data: "order1=" + order1 + "&order2=" + order2,
dataType: "json",
success: function(data) {}
});
*/
var newItem1 = determineChange(items.pre[1], items.post[1]);
console.log(newItem1);
}
}).disableSelection();
// Activation Order
items.act[1] = $('#sortable1').sortable('toArray');
items.act[2] = $('#sortable2').sortable('toArray');
log();
});
Leaving them as Arrays makes it a lot easier to compare and manipulate. Storing each part in an object simply makes it easier to gather all the info.
The only thing to be mindful of is that update runs 2 times. When an item is removed from list 1, out and added to list 2, receive. It does not make a difference in the end, but it's something to be mindful of just in case.

radomize ul tag not working

this is probably an easy question for you guys but I'm very new to coding and can't figure out this. I have a code that I want to randomize the given choices in the questions, and I've found a script online that does that but it's not working. I don't know what the
// shuffle only elements that don't have "group" class
$ul.find("li[class!='single_question', 'question', 'title', 'text']").each(function() {
means so I tried to put all id that I don't need to randomize in it but it's still not working.
Can someone help me this please? Also is there anyway I can add choice "A", choice "B", choice "C", and choice "D" in front of each given options so even after the options(answers) are randomized, the A,B,C,D options will still be in order? Thank you. Here's the code:
HTML:
<!DOCTYPE html>
<html>
<body>
<script src="JQ.js"></script>
<script src="function.js"></script>
<link href="style.css" rel="stylesheet" />
<div id="quiz_container">
<ul class="quiz_container">
<li class="single_question" data-question-id="1" data-correct-answer="1">
<div class="question">
<h1 class="title">P.1 Grammar Review</h1>
<p class="text">1. "What is your name__"</p>
</div>
<ul class="options">
<li value="1">?</li>
<li value="2">.</li>
<li value="3">,</li>
</ul>
<div class="result"></div>
</li>
<li class="single_question" data-question-id="2" data-correct-answer="b">
<div class="question">
<p class="text">2. "Do you like the banana__"</p>
</div>
<ul class="options">
<li value="a">.</li>
<li value="b">?</li>
<li value="c">,</li>
</ul>
<div class="result"></div>
</li>
</div>
</body>
</html>
JS:
$(document).ready(function () {
/*
* shuffles the array
* #param {Array} myArray array to shuffle
*/
function shuffleArray(myArray) {
for (var i = myArray.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = myArray[i];
myArray[i] = myArray[j];
myArray[j] = temp;
}
return myArray;
}
var $ul, $li, li_content, li_list;
// find all lists to shuffle
$("#quiz_container > ul").each(function () {
$ul = $(this);
li_list = [];
// shuffle only elements that don't have "group" class
$ul.find("li[class!='single_question', 'question', 'title', 'text']").each(function () {
// add content to the array and remove item from the DOM
li_list.push($(this).html());
$(this).remove();
});
// shuffle the list
li_list = shuffleArray(li_list);
while (li_content = li_list.pop()) {
// create <li> element and put it back to the DOM
$li = $("<li />").html(li_content);
$ul.append($li);
}
});
$("#contact_div").show();
});
$(document).on('click', '.single_question .options li', function () {
// Save the question of the clicked option
question = $(this).parents('.single_question');
// Remove If Anyother option is already selected
question.find('.selected').removeClass('selected');
// Add selected class to the clicked li
$(this).addClass('selected');
// selected option value
selected_answer_value = $(this).attr("value");
// Value of correct answer from '.single-question' attribute
correct_answer_value = question.attr("data-correct-answer");
correct_answer_text = question.find('.options').find("li[value='" + correct_answer_value + "']").text();
if (correct_answer_value == selected_answer_value)
result = "<div class='correct'> Correct ! </div>";
else
result = "<div class='wrong'> Correct answer is -> " + correct_answer_text + "</div>";
// Write the result of the question
$(this).parents('.single_question').find('.result').html(result);
// Calculate the score
score_calculator();
});
/**
* It loops through every question and increments the value when "data-correct-answer" value and "option's value" are same
*/
function score_calculator() {
score = 0;
$('.single_question').each(function () {
question = $(this);
if (question.attr('data-correct-answer') == question.find('.selected').attr("value")) {
score++;
}
});
$('.correct_answers').html(score);
}
It looks like you're using jQuery, even though the question isn't tagged as such. If that's the case, you can use a code snippet written by Chris Coyier of CSS-Tricks called shuffle children.
Here's an example of the code in action.
$.fn.shuffleChildren = function() {
$.each(this.get(), function(index, el) {
var $el = $(el);
var $find = $el.children();
$find.sort(function() {
return 0.5 - Math.random();
});
$el.empty();
$find.appendTo($el);
});
};
$("ul.randomized").shuffleChildren();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>Static List:</h4>
<ul>
<li>First element</li>
<li>Second element</li>
<li>Third element</li>
<li>Fourth element</li>
</ul>
<h4>Randomized List:</h4>
<ul class="randomized">
<li>First element</li>
<li>Second element</li>
<li>Third element</li>
<li>Fourth element</li>
</ul>
In order to apply it to your own code, all you'd need to do is modify the CSS selector at the bottom of the jQuery snippet. In your case, ul.options might be a good choice.
Here are a couple of examples using your markup:
jsFiddle
Self-Contained HTML Doc

Current list collapsible and parent elements selectable

I am having issues making my lists interact the way I need them to.
I need to keep the functionality I currently have While adding in two more key parts.
I have been unsuccessful at adding these parts without losing current functionality, or completely breaking the application.
I need to be able to select/deselect all the children elements by clicking the parent.
I need the lists to be collapsible, and When a parent has a child on the second list I need the parent's name to be above the children on that list.
If all children are off the list on the right then the parent shouldn't appear there, however the parent should always be on the left.
The issue I've really been running into is that to make it collapsible I've been trying to use check boxes, and hide the styling, but none of that worked, and you'll see why. code below.
In summary
Collapsible Lists
Select/Deselect all children by clicking parent
parent id appears on left list if 1 or more children are on it.
Fiddle
HTML:
<div class="half">
<ol id="one">
<li id="alaska">
Alaska
<ul>
<li data-id="alaska"><input type="checkbox"> Children 1</li>
<li data-id="alaska"><input type="checkbox"> Children 2</li>
<li data-id="alaska"><input type="checkbox"> Children 3</li>
</ul>
</li>
<li id="washington">
Washington
<ul>
<li data-id="washington"><input type="checkbox"> Children 1</li>
<li data-id="washington"><input type="checkbox"> Children 2</li>
<li data-id="washington"><input type="checkbox"> Children 3</li>
</ul>
</li>
<li id="texas">
Texas
<ul>
<li data-id="texas"><input type="checkbox"> Children 1</li>
<li data-id="texas"><input type="checkbox"> Children 2</li>
<li data-id="texas"><input type="checkbox"> Children 3</li>
</ul>
</li>
</ol>
<button type="button" class="include">Include</button>
</div>
<div class="half">
<ol id="two"></ol>
<button type="button" class="exclude">Exclude</button>
</div>
CSS:
.half {
display:block;
float:left;
width:50%;
}
JavaScript:
$(function(){
$('.include').click(function(e){
var $checks = $("input:checked");
$.each($checks, function(k,v){
var tempid = $(this).parent().data('id');
var element = $(this).parent().detach();
$('#two').append(element);
});
});
$('.exclude').click(function(e){
var $checks = $("input:checked");
$.each($checks, function(k,v){
var tempid = $(this).parent().data('id');
var element = $(this).parent().detach();
$('#one').find('#'+tempid+' ul').append(element);
});
});
});
Fiddle
Update
Here is an updated demo https://jsfiddle.net/dhirajbodicherla/hb9j8ua7/19/
Made few changes to .exclude method
$('.exclude').click(function(e) {
var $checks = $("input:checked");
$.each($checks, function(k, v) {
var tempid = $(this).parent().data('id');
var element = $(this).parent().detach();
$('#one').find('#' + tempid + ' ul').append(element);
var items = $('#two #' + tempid + '-2').children('li');
if (items.length == 3 || items.length == 0) {
$('#two #' + tempid + '-2-heading').hide();
} else {
$('#two #' + tempid + '-2-heading').show();
}
});
});
Here is a demo https://jsfiddle.net/dhirajbodicherla/hb9j8ua7/16/
Something like this should do
HTML structure would look like this
I changed it a bit to accommodate collapse and checking all children.
<li id="alaska">
<span class="parent">Alaska</span>
<span class="collapse">collapse</span>
<ul>
<li data-id="alaska">
<input type="checkbox">Children 1</li>
<li data-id="alaska">
<input type="checkbox">Children 2</li>
<li data-id="alaska">
<input type="checkbox">Children 3</li>
</ul>
</li>
Include script
$('.include').click(function(e) {
var $checks = $("input:checked");
$.each($checks, function(k, v) {
var tempid = $(this).parent().data('id');
var element = $(this).parent().detach();
if (!$('#two #' + tempid + '-2').length) {
var ol = $('<ol></ol>').attr('id', tempid + '-2');
var heading = $('<span />').text(tempid).attr('id', tempid + '-2-heading');
ol.prepend(heading);
$('#two').append(ol);
}
$('#two #' + tempid + '-2').append(element);
if ($('#two #' + tempid + '-2').children('li').length == 3) {
$('#two #' + tempid + '-2-heading').hide();
} else {
$('#two #' + tempid + '-2-heading').show();
}
});
});
When the include button is clicked the script will create a new ordered list based on the checkboxes that are selected. The new ordered lists under #two would look like this #alaska-2. This is achieved by
if (!$('#two #' + tempid + '-2').length) { // check if alaska-2 is present
var ol = $('<ol></ol>').attr('id', tempid + '-2');
var heading = $('<span />').text(tempid).attr('id', tempid + '-2-heading');
ol.prepend(heading);
$('#two').append(ol);
}
Apart from creating a new ordered list #alaska-2 that script would add a span which would be the name of the parent and it would have an id like #alaska-2-heading.
If all the children are selected then the heading would be hidden which is achieved by this script (which is the same in exclude script too)
if ($('#two #' + tempid + '-2').children('li').length == 3) {
$('#two #' + tempid + '-2-heading').hide();
} else {
$('#two #' + tempid + '-2-heading').show();
}
exclude script
$('.exclude').click(function(e) {
var $checks = $("input:checked");
$.each($checks, function(k, v) {
var tempid = $(this).parent().data('id');
var element = $(this).parent().detach();
$('#one').find('#' + tempid + ' ul').append(element);
if ($('#two #' + tempid + '-2').children('li').length == 3) {
$('#two #' + tempid + '-2-heading').hide();
} else {
$('#two #' + tempid + '-2-heading').show();
}
});
});
check all children
$('#one span.parent').on('click', function() {
var checked = $(this).parent().find('input').prop('checked');
$(this).parent().find('input').prop('checked', !checked);
});
collapse children
$('#one span.collapse').on('click', function() {
$(this).parent().find('ul').slideToggle();
});
Here's the fiddle solution: https://jsfiddle.net/hb9j8ua7/1/
I'll explain the important bits here.
HTML for single list item
<li id="alaska">
<span class="title">Alaska</span><span class="collapse">(V)</span>
<ul>
<li data-id="alaska"><input type="checkbox" /> Children 1</li>
<li data-id="alaska"><input type="checkbox" /> Children 2</li>
<li data-id="alaska"><input type="checkbox" /> Children 3</li>
</ul>
</li>
I've added wrapped the title in a title span, and added a button to toggle collapse of the list.
JS for include
$('.include').click(function(e){
var $checks = $("#one input:checked");
$.each($checks, function(k,v){
var $parent = $(this).parent();
var tempid = $parent.data('id');
var element = $parent.detach();
element.prepend('<span class="state-name">' + tempid + '</span><br>');
$('#two').append(element);
});
});
Refactored a bit to capture the parent in a variable only once. Prepended a span classed state-name to display the state name above the included children.
JS for collapse and auto-select
$('.collapse').click(function() {
$(this).siblings('ul').slideToggle();
});
$('.title').click(function() {
$(this).siblings('ul').find('input').each(function() {
$(this).attr('checked', true);
});
});
Clicking the collapse button will open and close the sibling ul
Clicking the title will mark all children checkboxes as checked.
I believe this takes care of all the issues you were facing.

Jquery tabs: change ajax data on tab click

I have used jquery tabs for loading data with ajax, but I need to add some parameters to the url when the user clicks in a tab. I don't know the parameters in advance because they come from a form which has been filled by the user. So I've tried something like the following code, but I don't get it working:
<div id="tabs">
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
</div>
and I serialize the form in an array and join the array to the array containing the satic data.
var staticData = [{name:'id',value:'${myId}'}];
$("#tabs").tabs({
var $tabs = $("#tabs").tabs({
ajaxOptions: { data: staticData},
select: function(event, ui) {
var dynamicData = $("#common_form").serializeArray();
var dataToSend = staticData.concat(dynamicData);
$("#tabs").tabs("option", "ajaxOptions", { 'data': dataToSend });
return true;
}
});
});
but this doesn't update the ajax data after the tabs are created (I'm seeing the request sent with Firebug and it only includes the initial params).
How can I change the ajax data when the user clicks the tab?
Thanks
EDITED: now with this code works
I think that what you are looking for is the "url" method :
http://jqueryui.com/demos/tabs/#method-url
You code would look something like that :
$(function(){
$("#tabs").tabs({
select: function(event, ui) {
var data = $("#common_form").serialize();
if(ui.index == 1){
var url = '${tab2_url}' + "&" + data;
$("#tabs").tabs("url", 1, url); //this is new !
}
return true;
}
});
});
jQuery(document).ready(function($){
$("a[name=tab]").click(function(e){
if($(this).attr('id')=="tab1")
{
//pass url1
}
if($(this).attr('id')=="tab2")
{
//pass url2
}
if($(this).attr('id')=="tab3")
{
//pass url3
}
});
});
This work for me but if i need for ex 100 tabs i need to put in all 100 tabs url.
<div id="tabs">
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
</div>
Jquery code...
jQuery(document).ready(function($){
$("a[name=tab]").click(function(e){
if($(this).attr('id')=="tab1")
{
//pass url1
}
if($(this).attr('id')=="tab2")
{
//pass url2
}
if($(this).attr('id')=="tab3")
{
//pass url3
}
});
});

Categories