Access the data-id of both the element dragged and the element replaced using SortableJS - javascript

I can access the id of the element dragged (see the code below) but not sure how to also grab the id of the element replaced.
the JS:
Sortable.create(selection, {
handle: '.bars-move',
animation: 150,
onStart: function (/**Event*/evt) {
var itemEl = evt.item;
var data_id = itemEl.getAttribute("data-id");
},
});
Any ideas please?

On onStart save the current list of ids, and on onEnd refer to this list using evt.newIndex.
var originalList;
var sortable = new Sortable(document.getElementById('items'), {
onStart: function(evt) {
originalList = [...document.querySelectorAll("#items > div")].map(el => el.id);
},
onEnd: function(evt) {
console.log("Dragged id: " + evt.item.id +
" - Replaced id: " + originalList[evt.newIndex]);
}
});
.list-group-item { font-size: 10px; padding: 1px 5px!important; width: 150px }
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.10.1/Sortable.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
<div id="items" class="list-group col">
<div id="item1" class="list-group-item">Item 1</div>
<div id="item2" class="list-group-item">Item 2</div>
<div id="item3" class="list-group-item">Item 3</div>
<div id="item4" class="list-group-item">Item 4</div>
<div id="item5" class="list-group-item">Item 5</div>
<div id="item6" class="list-group-item">Item 6</div>
</div>

Related

jQuery: alternate switching of classes in several div blocks

I have 2 identical div blocks on the page, inside of which there are several div blocks with different texts. It looks something like this:
<div class="dynamic_titles">
<div class="dynamic_title active">text 1</div>
<div class="dynamic_title inactive">text 2</div>
<div class="dynamic_title inactive">text 3</div>
<div class="dynamic_title inactive">text 4</div>
<div class="dynamic_title inactive">text 5</div>
</div>
<div class="dynamic_titles">
<div class="dynamic_title active">text 6</div>
<div class="dynamic_title inactive">text 7</div>
<div class="dynamic_title inactive">text 8</div>
<div class="dynamic_title inactive">text 9</div>
<div class="dynamic_title inactive">text 10</div>
</div>
I need to change classes from inactive to active in each dynamic_title block in turn. That is, the next one takes active, and the previous one with active in inactive and so on in a circle.
When I have one dynamic_titles block, everything works fine, but 2 interfere with each other, break. I'm trying not to make 2 functions for each block separately, I want to make one universal one that would be applied to each block separately.
Now I have such a code, but it does not work as expected. It runs once and goes no further. If done without .each, it works, but again not as expected and eventually breaks. What is my mistake?
$('.dynamic_titles').each(function () {
var index = 1,
max = $(this).find('.dynamic_title').length;
function setActiveHeadline() {
setTimeout(function () {
$(this).find('.active').removeClass('active').addClass('inactive');
index++;
if (index > max) {
index = 1;
}
$(this).find('.dynamic_title:nth-of-type(' + index + ')').addClass('active').removeClass('inactive');
setActiveHeadline();
}, 3000);
}
setActiveHeadline();
widthTitle = $(this).find('.dynamic_title.active').width();
$(this).css({
'width': widthTitle,
});
function setWidthHeadlines() {
setTimeout(function () {
widthTitle = $(this).find('.dynamic_title.active').width();
$(this).css({
'width': widthTitle,
});
setWidthHeadlines();
}, 3000);
}
setWidthHeadlines();
})
Thank you for your help!
I think you could simplify this into one function that uses setInterval, please see the following snippet:
$(function(){
//use setInterval to continue the changes
setInterval(() => {
//iterate the blocks
$('div.dynamic_titles').each(function() {
//get the children
const $children = $(this).find('div.dynamic_title');
//get the number of children
const numofchildren = $children.length;
//index for switching active in children
let activeindex = 0;
//iterate children to find the active one
$children.each(function(i, el) {
//if active, remove active and add inactive classNames
//store the index
//break the each loop
if($(el).hasClass('active')){
$(el).removeClass('active').addClass('inactive');
activeindex = i + 1;
return false;
}
});
//if index has reached the last child, reset to zero
if(activeindex > numofchildren - 1){
activeindex = 0;
}
//set the next child to active and remove inactive className
$children[activeindex].classList.add('active');
$children[activeindex].classList.remove('inactive');
});
}, 3000);
})
.active {
color:green;
}
.inactive {
color:grey;
}
<div class="dynamic_titles">
<div class="dynamic_title active">text 1</div>
<div class="dynamic_title inactive">text 2</div>
<div class="dynamic_title inactive">text 3</div>
<div class="dynamic_title inactive">text 4</div>
<div class="dynamic_title inactive">text 5</div>
</div>
<div class="dynamic_titles">
<div class="dynamic_title active">text 6</div>
<div class="dynamic_title inactive">text 7</div>
<div class="dynamic_title inactive">text 8</div>
<div class="dynamic_title inactive">text 9</div>
<div class="dynamic_title inactive">text 10</div>
</div>
<script
src="https://code.jquery.com/jquery-3.6.1.js"
integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
crossorigin="anonymous"></script>

Javascript forEach loop with current index after new element was deleted

I am trying to get the updated index of the forEach loop after an element has been removed from the DOM.
Here is my current script ...
function delete(){
var elm = document.querySelectorAll('.divs');
elm.forEach(function(item, index){
item.addEventListener('click', function(e){
document.querySelectorAll('.divs')[index].remove();
});
}
after the element was removed the calculations for the index are incorrect.
how can I get the index of the clicked item after one or more items has been removed?
I've a very simple solution, but I don't know if you can accept it!
Node list in DOM is always live, if you refer to it as a variable, this variable is active. Check here:
https://developer.mozilla.org/en-US/docs/Web/API/NodeList
I propose you just to hide it!
listDivs1();
deleteDiv1();
function listDivs1() {
let ht = '';
let els = document.querySelectorAll('#method1 .divs');
els.forEach(function(e, index) {
ht += 'index: ' + index + ' - ' + e.innerHTML + '<br>';
});
document.querySelector('.elements1').innerHTML = ht;
}
function deleteDiv1() {
let els = document.querySelectorAll('#method1 .divs');
els.forEach(function(item, index) {
item.addEventListener('click', function(e) {
document.querySelectorAll('.divs')[index].remove();
listDivs1();
});
});
}
listDivs2();
deleteDiv2();
function listDivs2() {
let ht = '';
let els = document.querySelectorAll('#method2 .divs');
els.forEach(function(e, index) {
ht += 'index: ' + index + ' - ' + e.innerHTML + '<br>';
});
document.querySelector('.elements2').innerHTML = ht;
}
function deleteDiv2() {
let els = document.querySelectorAll('#method2 .divs');
els.forEach(function(item, index) {
item.addEventListener('click', function(e) {
e.target.remove();
listDivs2();
});
});
}
listDivs3();
deleteDiv3();
function listDivs3() {
let ht = '';
let els = document.querySelectorAll('#method3 .divs');
els.forEach(function(e, index) {
if (e.style.display !== 'none') {
ht += 'index: ' + index + ' - ' + e.innerHTML + '<br>';
}
});
document.querySelector('.elements3').innerHTML = ht;
}
window.addEventListener('load', listDivs3);
function deleteDiv3() {
let els = document.querySelectorAll('#method3 .divs');
els.forEach(function(item, index) {
item.addEventListener('click', function(e) {
e.target.style.display = 'none';
listDivs3();
});
});
}
.container {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: 1fr;
grid-column-gap: 0px;
grid-row-gap: 0px;
padding: 1em;
}
<h1>your delete</h1>
<div class="container">
<div id="method1">
<div class="divs">div 0</div>
<div class="divs">div 1</div>
<div class="divs">div 2</div>
<div class="divs">div 3</div>
<div class="divs">div 4</div>
<div class="divs">div 5</div>
<div class="divs">div 6</div>
<div class="divs">div 7</div>
<div class="divs">div 8</div>
<div class="divs">div 9</div>
</div>
<div class="elements1"></div>
</div>
<hr>
<h1>e.target</h1>
<div class="container">
<div id="method2">
<div class="divs">div 0</div>
<div class="divs">div 1</div>
<div class="divs">div 2</div>
<div class="divs">div 3</div>
<div class="divs">div 4</div>
<div class="divs">div 5</div>
<div class="divs">div 6</div>
<div class="divs">div 7</div>
<div class="divs">div 8</div>
<div class="divs">div 9</div>
</div>
<div class="elements2"></div>
</div>
<hr>
<h1>display none</h1>
<div class="container">
<div id="method3">
<div class="divs">div 0</div>
<div class="divs">div 1</div>
<div class="divs">div 2</div>
<div class="divs">div 3</div>
<div class="divs">div 4</div>
<div class="divs">div 5</div>
<div class="divs">div 6</div>
<div class="divs">div 7</div>
<div class="divs">div 8</div>
<div class="divs">div 9</div>
</div>
<div class="elements3"></div>
</div>
In the snippet you 2 columns, the one with the divs to click, and second one with index and div content to check after each delete the result.
So with your function, index and div disconnect. With e.target same. With display none, it's working because nothing has been really removed, only "visually" removed. If you have other functions iterating through all those divs you'll have to exclude the display none like in listDivs3 function.
If this display none doesn't suit you, The only way would be:
store all div in temporary variable
remove the one you need
remove the node list
put the divs from the new list
re add your even listener

JavaScript Sortable do function on list changes?

How can I make a function fire when a sortable list changes?
I have looked for some examples but they are making the lists different to how I am and I cant see how to do it with how my sortable lists are generated. Perhaps I am using a different Sortable version or something?
This is how I make my lists...
<script>
Sortable.create(plansList, {
animation: 100,
group: 'list-1',
draggable: '.recipe',
handle: '.handle',
sort: true,
filter: '.sortable-disabled',
chosenClass: 'active'
});
Sortable.create(suggestionsList, {
animation: 100,
group: 'list-1',
draggable: '.recipe',
handle: '.handle',
sort: true,
filter: '.sortable-disabled',
chosenClass: 'active'
});
</script>
As you can see I have 2 lists. I want both to fire the same function when they change, but if both lists change (because an item was moved from one list to the other) I don't want the function to fire twice.
Can anyone help?
Thanks,
Chris
Event onEnd will only fire once, as requested. But it will fire if also no change has been made. So you might need to compare old index to new index.
var example2Left = document.querySelector("#example2-left");
var example2Right = document.querySelector("#example2-right");
new Sortable(example2Left, {
group: 'shared', // set both lists to same group
animation: 150,
onEnd: foo
});
new Sortable(example2Right, {
group: 'shared',
animation: 150,
onEnd: foo
});
function foo(ev) {
var to = ev.to;
var from = ev.from;
var oldIndex = ev.oldIndex;
var newIndex = ev.newIndex;
if (to != from || oldIndex != newIndex) {
singleOnChange(ev)
}
}
function singleOnChange(ev) {
console.log("list(s) changed")
}
.col {
width: 50%;
float: left;
}
.list-group-item {
padding: 10px;
margin: 2px;
background: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js" integrity="sha512-Eezs+g9Lq4TCCq0wae01s9PuNWzHYoCMkE97e2qdkYthpI0pzC3UGB03lgEHn2XM85hDOUF6qgqqszs+iXU4UA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="example2-left" class="list-group col">
<div class="list-group-item">Item 1</div>
<div class="list-group-item">Item 2</div>
<div class="list-group-item">Item 3</div>
<div class="list-group-item">Item 4</div>
<div class="list-group-item">Item 5</div>
<div class="list-group-item">Item 6</div>
</div>
<div id="example2-right" class="list-group col">
<div class="list-group-item">Item 1</div>
<div class="list-group-item">Item 2</div>
<div class="list-group-item">Item 3</div>
<div class="list-group-item">Item 4</div>
<div class="list-group-item">Item 5</div>
<div class="list-group-item">Item 6</div>
</div>

Onclick scroll to next div with a specific ID

$('.mark').click(function(){
var id = $(this).attr('id');
var next = (Number(id) + 1);
window.scrollTo($('#'+next));
})
.mark {
height: 500px;
color: white;
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='marker'>
<div class='mark' id='1'>Mark 1</div>
<div class='mark' id='2'>Mark 2</div>
<div class='mark' id='3'>Mark 3</div>
<div class='mark' id='4'>Mark 4</div>
<div class='mark' id='5'>Mark 5</div>
</div>
Right here, I'm trying to make my window scroll to the next div with an id numbered with current id + 1.
I've tried window.scrollTo($('#'+next));.
But didn't work, then tried window.scrollTo($('#'+next), 500);.
But always returns to first div + 500px.
What is the proper way to handle that?
window.scrollTo(xpos, ypos) takes two parameters. The first one is coordinate along x-axis and second is along y-axis. So you can use 0 for first parameter and $('#'+next).offset().top for second parameter
$('.mark').click(function(){
var id = $(this).attr('id');
var next = (Number(id) + 1);
//window.scrollTo(0, $('#'+next).offset().top);
$('html, body').animate({scrollTop : $('#'+next).offset().top}, 2000);
})
.mark {
height: 500px;
color: white;
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='marker'>
<div class='mark' id='1'>Mark 1</div>
<div class='mark' id='2'>Mark 2</div>
<div class='mark' id='3'>Mark 3</div>
<div class='mark' id='4'>Mark 4</div>
<div class='mark' id='5'>Mark 5</div>
</div>
I have done something like this which maybe helpfull for you check out.
HTML
<div class='marker'>
<div class='mark' id='1'>Mark 1</div>
<div class='mark' id='2'>Mark 2</div>
<div class='mark' id='3'>Mark 3</div>
<div class='mark' id='4'>Mark 4</div>
<div class='mark' id='5'>Mark 5</div>
</div>
CSS
.mark {
height: 500px;
color: white;
background: black;
}
JS
$(document).ready(function(){
$('.mark').click(function(){;
var y = $(this).outerHeight();
var z =$(window).scrollTop();
$('body').animate({scrollTop:z+y},800);
});
});
You can check the demo here
$('.mark').click(function(){
var id = $(this).attr('id');
var next = (Number(id) + 1);
location.href="#"+next;
});
.mark {
height: 500px;
color: white;
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='marker'>
<div class='mark' id='1'>Mark 1</div>
<div class='mark' id='2'>Mark 2</div>
<div class='mark' id='3'>Mark 3</div>
<div class='mark' id='4'>Mark 4</div>
<div class='mark' id='5'>Mark 5</div>
</div>
This is the solution when you use data-number.
Actually, it's not perfect solution.
But I hope it help you.
$('.mark').click(function(){
var id = $(this).attr('data-number');
var next = (Number(id) + 1);
window.scrollTo(0, $(`.mark[data-number=${next}]`).offset().top);
});
.mark {
height: 500px;
color: white;
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='marker'>
<div class='mark' data-number='1'>Mark 1</div>
<div class='mark' data-number='2'>Mark 2</div>
<div class='mark' data-number='3'>Mark 3</div>
<div class='mark' data-number='4'>Mark 4</div>
<div class='mark' data-number='5'>Mark 5</div>
</div>

Assign multiple elements random size and random placement, while following the sequential div order

Trying to figure out somekind of logic that will let me distribute elements in a sequenced order. What size and position should be randomly generated within a given range (for example, element must be within viewport and the size of the element should be within a defined range such as minimum 10% of viewport, maximum 60%).
I'm not sure what would be the best way to approach something like this, any ideas?
I've attached a sketch below of what it could look like. The layout would be different at each load.
Markup
<div class="container">
<div class="post">Post 1</div>
<div class="post">Post 2</div>
<div class="post">Post 3</div>
<div class="post">Post 4</div>
<div class="post">Post 5</div>
<div class="post">Post 6</div>
<div class="post">Post 7</div>
<div class="post">Post 8</div>
<div class="post">Post 9</div>
<div class="post">Post 10</div>
<div class="post">Post 11</div>
<div class="post">Post 12</div>
<div class="post">Post 13</div>
<div class="post">Post 14</div>
<div class="post">Post 15</div>
</div>
Thanks, this was fun to code. I set some defaults in CSS and then did the logic in JS. Note that the buffer() function is nonlinear; we want most of the buffers to be close to zero and very few of them to be on the larger side, so we use powers of e (2.7⁰/12 = 0.08em to 2.7⁶/12 = 33.62em) for the scale and then multiply them out to get a bigger range of numbers (rather than powers of two divided by 12).
I assume the elements are already chronologically ordered. If not, that shouldn't be hard to do, just sort posts[] and insert them in order using appendChild() on the container.
var defaultMax = Math.exp(6) / 12; // e⁶ / 12 = 33.5em
// random buffer to give for spacing.
// growth is inverse exponential, so larger is less likely
function buffer(min=0.1, max=defaultMax, mult=1) {
return Math.min(max, Math.max(min,
min / 2 + Math.exp(Math.random() * 6) * Math.random() * mult / 12
))+"em";
}
function randomize() {
var posts = document.getElementsByClassName("posts");
for (var p = 0; p < posts.length; p++) {
// random buffered margins, ordered: top right bottom left.
// top is at least 0.1em, right and bottom are at least 0.25em.
// top and bottom are cut in half to limit lost vertical space.
posts[p].style.margin = buffer(0.1, defaultMax, 0.5) + " "
+ buffer(0.25) + " "
+ buffer(0.25, defaultMax, 0.5) + " "
+ buffer();
// random width and height (with sane minimum size: 8em x 5em)
posts[p].style.width = buffer(8);
posts[p].style.height = buffer(5);
}
}
.posts { float:left; background:#000; color:#fff;
padding:0.2em; text-align:center; }
.container { width:50em; max-width:100%; }
<body onload="randomize()">
<div class="container">
<div class="posts">Post 1</div>
<div class="posts">Post 2</div>
<div class="posts">Post 3</div>
<div class="posts">Post 4</div>
<div class="posts">Post 5</div>
<div class="posts">Post 6</div>
<div class="posts">Post 7</div>
<div class="posts">Post 8</div>
<div class="posts">Post 9</div>
<div class="posts">Post 10</div>
<div class="posts">Post 11</div>
<div class="posts">Post 12</div>
<div class="posts">Post 13</div>
<div class="posts">Post 14</div>
<div class="posts">Post 15</div>
</div>
</body>
So I've used Adams code as a base. So HTML and CSS stay the same:
HTML
<body onload="randomize()">
<div class="container">
<div class="posts">Post 1</div>
<div class="posts">Post 2</div>
<div class="posts">Post 3</div>
<div class="posts">Post 4</div>
<div class="posts">Post 5</div>
<div class="posts">Post 6</div>
<div class="posts">Post 7</div>
<div class="posts">Post 8</div>
<div class="posts">Post 9</div>
<div class="posts">Post 10</div>
<div class="posts">Post 11</div>
<div class="posts">Post 12</div>
<div class="posts">Post 13</div>
<div class="posts">Post 14</div>
<div class="posts">Post 15</div>
</div>
</body>
CSS:
.posts { float:left; background:#000; color:#fff; padding:0.2em; text-align:center; }
.container { width:50em; max-width:100%; }
JavaScript
Will change however. Update any of the properties in the options variable to change layout.
var options = {
width: {
min: 10,
max: 60,
unit: '%'
},
height: {
min: 10,
max: 30,
unit: '%'
},
margin: {
min: 5,
max: 10,
unit: 'px'
}
}
function getRandomInt (min, max, unit) {
return Math.floor(Math.random() * (max - min + 1)) + min + unit;
}
function randomize() {
var posts = document.getElementsByClassName("posts");
for (var p = 0; p < posts.length; p++) {
posts[p].style.margin = getRandomInt(options.margin.min,options.margin.max, options.margin.unit) + " " + getRandomInt(options.margin.min,options.margin.max, options.margin.unit);
posts[p].style.width = getRandomInt(options.width.min,options.width.max, options.width.unit);
posts[p].style.height = getRandomInt(options.height.min,options.height.max, options.height.unit);
}
}
I've created a fiddle: http://jsfiddle.net/qsUw7/

Categories

Resources