Javascript drag and drop with one elemant - javascript

I'm currently learning JavaScript and have created code allowing a user to drag and drop an item. However, this code requires there to be two objects in the list of objects when I want there only to be one. I believe the issue is due to using for loops which start from 0 and require the length of the list of items to be greater than 0. But due to lists starting from a 0 index, this doesn't happen. My code is:
const list_items = document.querySelectorAll('.list-item');
const lists = document.querySelectorAll('.list');
let draggedItem = null;
for (let i = 0; i < list_items.length; i++) {
const item = list_items[i];
item.addEventListener('dragstart', function () {
draggedItem = item;
setTimeout(function () {
item.style.display = 'none';
}, 0)
});
item.addEventListener('dragend', function () {
setTimeout(function () {
draggedItem.style.display = 'block';
draggedItem = null;
}, 0);
})
for (let j = 0; j < lists.length; j ++) {
const list = lists[j];
list.addEventListener('dragover', function (e) {
e.preventDefault();
});
list.addEventListener('dragenter', function (e) {
e.preventDefault();
});
list.addEventListener('drop', function (e) {
console.log('drop');
this.append(draggedItem);
});
}
}

Just an idea, so it might not work: have you tried changing your for loops from
(let j = 0; j < lists.length; j ++)
to
(let j = 0; j <= lists.length; j ++)
<= in JS is less than or equal to

Related

How can I create a real lazy loading in Shopify

I have this code and its running. Its basically hiding the elements that are not in view and when they come into view the .lazy class is removed and they become visible. The issue with this method is, that its still loading all the elements at once for example on page load.
How can I change the code that its really only loading the first 6 elements, then the user scrolls down and its loads the next 6 elements and so on. The important thing is, that the list has to stay searchable.
The perfect example would be this page here:
https://de.louisvuitton.com/deu-de/herren/taschen/alle-taschen/_/N-t1uezqf4
This is my code:
<script>
const batchSize = 3;
let observer;
let currentIndex = 0;
const showNextBatch = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const endIndex = currentIndex + batchSize;
for (let i = currentIndex; i < endIndex && i < items.length; i++) {
items[i].classList.remove('lazy');
}
currentIndex = endIndex;
if (currentIndex >= items.length) {
observer.disconnect();
}
}
});
};
const options = {
rootMargin: '50px 0px',
threshold: 0.01
};
if ('IntersectionObserver' in window) {
let collectionList = document.querySelector('.swiper-wrapper');
let items = collectionList.querySelectorAll('.swiper-slide');
for (let i = 0; i < items.length; i++) {
items[i].classList.add('lazy');
}
observer = new IntersectionObserver(showNextBatch, options);
for (let i = 0; i < batchSize; i++) {
items[i].classList.remove('lazy');
}
currentIndex = 0;
for (let i = currentIndex; i < items.length; i++) {
observer.observe(items[i]);
}
}
const mutationObserver = new MutationObserver(() => {
resetObserver();
});
mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
const resetObserver = () => {
observer.disconnect();
collectionList = document.querySelector('.swiper-wrapper');
items = collectionList.querySelectorAll('.swiper-slide');
for (let i = 0; i < items.length; i++) {
items[i].classList.add('lazy');
}
currentIndex = 0;
observer = new IntersectionObserver(showNextBatch, options);
for (let i = 0; i < batchSize; i++) {
items[i].classList.remove('lazy');
}
currentIndex = 0;
for (let i = currentIndex; i < items.length; i++) {
observer.observe(items[i]);
}
};
</script>

window.location.reload(); stuck in infinite loop

I want this function to loop through all items, if it finds the right item to load its page, and if it doesn't find the right item it should reload the page and for loop again. When I delete the window.location.reload(); it loads normally to the item page. This is the code:
var item_name = "Washed";
var item_color = "Red";
function pickItem() {
let items = document.getElementsByClassName("name-link");
for(i = 0; i < items.length; i++) {
if((items[i].innerHTML).includes(item_name)) {
for(j = 0; j < items.length; j++) {
if((items[j].innerHTML).includes(item_color)) {
if(items[i].href == items[j].href) {
window.location.assign(items[i, j].href);
}
}
}
}
}
window.location.reload();
}
In the following form it works as I want, but why does it need the chrome.storage function to work?(I used it with the chrome.storage before, but it was too slow for my purposes so I had to change it.)
var item_name = "Washed";
var item_color = "Red";
function pickItem() {
let items = document.getElementsByClassName("name-link");
chrome.storage.sync.get(["itemName", "color"], function(data) {
for(i = 0; i < items.length; i++) {
if((items[i].innerHTML).includes(item_name)) {
for(j = 0; j < items.length; j++) {
if((items[j].innerHTML).includes(item_color)) {
if(items[i].href == items[j].href) {
window.location.assign(items[i, j].href);
chrome.storage.sync.set({"item_url": items[i, j].href});
}
}
}
}
}
})
window.location.reload()
}
I'd recommend adding a boolean variable which indicates if a location to navigate to has been found yet and wrap the call to window.location.reload() inside an if-block which checks the state of the variable.
e.g.
function pickItem() {
let items = document.getElementsByClassName("name-link");
let found = false;
for (i = 0; i < items.length; i++) {
if ((items[i].innerHTML).includes(item_name)) {
for (j = 0; j < items.length; j++) {
if ((items[j].innerHTML).includes(item_color)) {
if (items[i].href == items[j].href) {
found = true;
window.location.assign(items[i, j].href);
}
}
}
}
}
if (!found) {
window.location.reload();
}
}

Problem with showing the results in my Calculator app with this.id

im having trouble using this.id as it doesnt return any value.
https://jsfiddle.net/o432v0L5/
I'm at the point where I want the number to be shown when I press it but it just doesnt work.
for (var i = 0; i < operator.length; i++) {
operator[i].addEventListener('click', function (e) {
alert(this.id)
})
}
var number = document.getElementsByClassName('number')
for (var i = 0; i < number.length; i++) {
number[i].addEventListener('click', function () {
var output = getOutput()
if (output != NaN) {
output = output + this.id;
printOutput(output)
}
})
}```
Have you tried
for (var i = 0; i < operator.length; i++) {
operator[i].addEventListener('click', function (e) {
alert(this.innerText);
})
}

Trigger an event by the execution of another one

I want to integrate the last two loops into the first two loops in order to trigger the events from the last ones when the first ones fire.
function expand() {
var coll = document.querySelectorAll(".grid-item");
for (x = 0; x < coll.length; x++) {
coll[x].addEventListener("mouseenter",
function () {
event.target.style.width = "480px";
});
}
for (x = 0; x < coll.length; x++) {
coll[x].addEventListener("mouseleave",
function () {
event.target.style.width = null;
});
}
var coll1 = document.querySelectorAll(".collapsing");
for (x = 0; x < coll1.length; x++) {
coll1[x].addEventListener("mouseenter",
function () {
event.target.style.maxHeight = "480px";
});
}
for (x = 0; x < coll1.length; x++) {
coll1[x].addEventListener("mouseleave",
function () {
event.target.style.maxHeight = null;
});
}
Maybe this helps you more than me as I am a js beginner...
With your for loop, you have the index of the .grid-items - simply select the item in the .collapsing nodeList at the same index, and then you can set its style as well:
const gridItems = document.querySelectorAll(".grid-item");
const collapsings = document.querySelectorAll(".collapsing");
for (let i = 0; i < gridItems.length; i++) {
gridItems[i].addEventListener("mouseenter", () => {
gridItems[i].style.width = "480px";
collapsings[i].style.maxHeight = "480px";
});
gridItems[i].addEventListener("mouseleave", () => {
gridItems[i].style.width = null;
collapsings[i].style.maxHeight = null;
});
}
(note the use of let i, that's very important - best to avoid implicitly creating global variables, and use const or let instead of var to avoid unintuitive behavior from hoisting/function scope)
Another option to consider, rather than changing styles manually like this, would be to toggle classes on and off instead. Also, if these elements are all in a container, you might think about using event delegation, that way you only ever add two events overall, rather than two events for every element.
Something like this?
function expand() {
var coll = document.querySelectorAll(".grid-item");
for (x = 0; x < coll.length; x++) {
coll[x].addEventListener("mouseenter",
function() {
event.target.style.width = "480px";
loop3(true);
});
}
for (x = 0; x < coll.length; x++) {
coll[x].addEventListener("mouseleave",
function() {
event.target.style.width = null;
loop4(true);
});
}
loop3(false);
function loop3(auto_trigger) {
var coll1 = document.querySelectorAll(".collapsing");
for (x = 0; x < coll1.length; x++) {
coll1[x].addEventListener("mouseenter",
function() {
event.target.style.maxHeight = "480px";
});
if (auto_trigger) coll1[x].style.maxHeight = "480px";
}
}
loop4(false);
function loop4(auto_trigger) {
var coll1 = document.querySelectorAll(".collapsing");
for (x = 0; x < coll1.length; x++) {
coll1[x].addEventListener("mouseleave",
function() {
event.target.style.maxHeight = null;
});
if (auto_trigger) coll1[x].style.maxHeight = null;
}
}
}

returning a value after for loops

So, I have been trying for the past few hours to get an result out of a function after performing some for loops :
Cluster.prototype.initiate_api_data_fetching = function(username) {
var self = this,
object = [];
return self.initiate_available_market_search(username, function(data_object){
var json_obj = JSON.parse(data_object);
for(var obj_key in json_obj) {
for (var i = json_obj[obj_key].length - 1; i >= 0; i--) {
self.initiate_market_items_data_fetching(username, json_obj[obj_key][i].site, function(data_obj){
var json_object = JSON.parse(data_obj);
for(var data_key in json_object) {
for (var j = json_object[data_key].length - 1; j >= 0; j--) {
object.push(json_object[data_key][j]);
/*log(object);*/
};
};
log(object);
});
};
};
});
};
Making abstraction of all the variables and other things that make no sense to you readers, I would just like to know how can I return the object array with the data that I\m pushing in it. Everything is fine if I\m logging where the /*log(object);*/ is, but if I want to see what the object contains at the end of the function, I get an empty array.
I suggest you add a callback to your main function and call it when done..
Cluster.prototype.initiate_api_data_fetching = function (username, callback) {
var self = this,
object = [];
return self.initiate_available_market_search(username, function (data_object) {
var json_obj = JSON.parse(data_object)
, counter = 0;
function done() {
counter -= 1;
if (counter === 0) {
callback(object);
}
}
for (var obj_key in json_obj) {
if (!json_obj.hasOwnProperty(obj_key)) { continue; }
for (var i = json_obj[obj_key].length - 1; i >= 0; i--) {
counter += 1;
self.initiate_market_items_data_fetching(username, json_obj[obj_key][i].site, function (data_obj) {
var json_object = JSON.parse(data_obj);
for (var data_key in json_object) {
if (!json_object.hasOwnProperty(data_key)) { continue; }
for (var j = json_object[data_key].length - 1; j >= 0; j--) {
object.push(json_object[data_key][j]);
/*log(object);*/
}
}
done();
});
}
}
});
};
PS. 1 assumption is that initiate_api_data_fetching is async.
PS. 2 Follow the advice from the commenters above to improve your code. I answered your immediate question by showing you how to synchronise async calls, but don't stop there.

Categories

Resources