Javascript - Fixing searching system? - javascript

Down below, there's two pieces of codes. For infinite scroll & search system. Everything works, but the problem with the search system is that, if I search something, then it messes up positioning of the cards or boxes. They should be on one line, if you search them, but those a bit up etc.. I also have added an picture about that. 2nd problem is that, I have an Infinite scroll on my site, but I think I would have to change the search system to search from JSON Data, so it would work correctly? By that I mean, You type something into search, click enter then it searches matches from the JSON and then shows them in that container. I hope I was clear enough about my problem and I hope someone can help me out to solve it :). Thanks to everyone! :)
Here's some CSS as well:
main.css - http://pastebin.com/Tgds0kuJ
zmd-hierarchical-display - http://pastebin.com/Fn5JBpaQ
Materialize - http://pastebin.com/ZxSGZtc8
Here's picture of normal piece: http://prntscr.com/b3yrwa
Here's picture if I search them: http://prntscr.com/b3yrub
Here's the infinite scroll & cards
var perPage = 50;
function paginate(items, page) {
var start = perPage * page;
return items.slice(start, start + perPage);
}
var condition = '';
function renderItems(pageItems) {
pageItems.forEach(function(item, index, arr) {
var message = 'BitSkins Price: $' + Math.round(item.bprice) + '';
if (item.price !== null) {
if (item.bprice === '') {
message = 'Item never sold on BitSkins!';
}
if (item.name != 'Operation Phoenix Case Key' && item.name != 'CS:GO Case Key' && item.name != 'Winter Offensive Case Key' && item.name != 'Revolver Case Key' && item.name != 'Operation Vanguard Case Key' && item.name != 'Operation Wildfire Case Key' && item.name != 'Shadow Case Key' && item.name != 'Operation Breakout Case Key' && item.name != 'Chroma Case Key' && item.name != 'Huntsman Case Key' && item.name != 'Falchion Case Key' && item.name != 'Chroma 2 Case Key') {
$("#inventory").html($("#inventory").html() + "<li class='col 2 zoomIn animated' style='padding:8px;font-weight:bold;font-size:13.5px'><div class='card item-card waves-effect waves-light' style='margin:0%;min-height:295px;width:245.438px;border-radius: 0px;height: 295px;box-shadow: inset 0px 0px 25px 2px #232323;border: 1px solid black' id='" + item.id + "'><div class='iteam' style='text-decoration: underline;text-align: left;font-size: 14.5px;color: #E8E8E8;font-family: Roboto;position: relative;right: -3px;'>" + item.name + "</div><div class='condition' style='text-align: left;color: #E8E8E8;font-family: Roboto;position: relative;left: 3px;'>" + item.condition + "</div><div class='center-align' style='position: relative;padding:0%;top: -33px;'><img title=\"" + item.originalname + "\" draggable='false' src='https://steamcommunity-a.akamaihd.net/economy/image/" + item.iconurl + "/235fx235f'></div><div class='secondarea' style='position: relative;top: -129px;background: rgba(0, 0, 0,0.15);display: block;height: 163px;'><div class='buyer-price center-align' style='font-size:22.5px;font-family: Arial Black;color:#E8E8E8'>$" + Math.round(item.price) + "<div class='bitskinscomp' style='font-weight: normal;font-size:12px;font-family: Roboto;font: bold;'>" + message + "</div></div><a class='btn waves-effect waves-light' style='position:relative;left:-5px;top:50px' href='" + item.inspect + "' target='_blank'>Inspect</a><a class='btn waves-effect waves-light' style='position:relative;right:-5px;top:50px' id='purchaseButton'>Cart</a></div></li>");
}
}
});
}
var win = $(window);
var page = 0;
renderItems(paginate(items, page));
win.scroll(function() {
if ($(document).height() - win.height() == win.scrollTop()) {
page++;
renderItems(paginate(items, page));
}
});
JavaScript search system
$('#SearchItemsFromList').keyup(function() {
var valThis = $(this).val().toLowerCase();
if (valThis === "") {
$('#inventory > li > div').show();
} else {
$('#inventory > li > div').each(function() {
var text = $(this).text().toLowerCase();
(text.indexOf(valThis) >= 0) ? $(this).show(): $(this).hide();
});
}
});

Overview
First off, I have no visibility to the
...messes up positioning of the cards or boxes...
since I do not know what your CSS consists of so I simply made some guesses on that. I suspect it may be due to the actual rendering of your items somehow. To assist in this I removed all the CSS from the injected markup as injecting those "style" attributes is not best practice and frankly difficult to debug as you seem to have experienced. I made an attempt but you will need to adjust the CSS I have provided as it simply does not have all yours in it.
To assist with this, I simply did a "replace" with the current page rather than append each time and then face the challenge of end of scroll/start and deal with the search disruption of that.
I removed the injection of duplicate id on the button and instead used a class injection instead. This will resolve the issue of the invalid HTML which would cause unexpected results at some point that would be very difficult to debug.
The more difficult issue is the dynamic nature of your items array when searching the on-page object list. This I have addressed by creation of a "view candidate list called currentSearch which I have taken the liberty of adding to a name called myApp.data as myApp.data.currentSearch.
Speaking of the namespace, I did that to avoid multiple global objects. I also did that with my custom functions as a best practice.
Here is my sample markup that I used:
<div id="search">
<input id="SearchItemsFromList" type="text" />
</div>
<ul id="inventory">
</ul>
CSS
Here is the CSS which in great part was extracted from the style properties. I took the liberty of naming them poorly as first-style-thing class, second-style-thing etc. which simply coordinate to the injected sequence of elements. This has the additional benefit of reducing the injection string size as well.
.li-style-thing {
padding: 8px;
font-weight: bold;
font-size: 13.5px;
}
.first-style-thing {
margin: 0%;
min-height: 295px;
width: 245.438px;
border-radius: 0px;
height: 295px;
box-shadow: inset 0px 0px 25px 2px #232323;
border: 1px solid black;
}
.second-style-thing {
text-decoration: underline;
text-align: left;
font-size: 14.5px;
color: #E8E8E8;
font-family: Roboto;
position: relative;
right: -3px;
}
.third-style-thing {
text-align: left;
color: #E8E8E8;
font-family: Roboto;
position: relative;
left: 3px;
}
.fourth-style-thing {
position: relative;
padding: 0%;
top: -33px;
}
.fifth-style-thing {
position: relative;
top: -129px;
background: rgba(0, 0, 0, 0.15);
display: block;
height: 163px;
}
.sixth-style-thing {
font-size: 22.5px;
font-family: Arial Black;
color: #E8E8E8;
}
.seventh-style-thing {
font-weight: normal;
font-size: 12px;
font-family: Roboto;
font: bold;
}
.eighth-style-thing {
position: relative;
left: -5px;
top: 50px;
}
.ninth-style-thing {
position: relative;
right: -5px;
top: 50px;
}
.btn {
position: relative;
display: block;
height: 1.5em;
width: 5em;
color: cyan;
background-color: blue;
font-weight: bold;
text-align: center;
padding-top: 0.5em;
margin: 1em;
text-decoration: none;
text-transform: uppercase;
}
#inventory {
display: block;
position: relative;
top: 1em;
left: 0em;
border: solid lime 1px;
}
#inventory li {
background-color: #888888;
}
#inventory li {
display: inline-block;
float: left;
}
.purchaseButton {
right: -8em;
top: 0;
}
#search {
height: 4em;
width: 100%;
background-color: #00aaaa;
padding: 1em;
}
Code:
About the code, note the items object which I simply made from reverse engineering your injection code and likely needs adjusted to your exact object properties.
Note the debounce function which addresses an issue where you might fire the scroll/mouse wheel events too often. I added a "throttle" which you might use instead, borrowed from here: https://remysharp.com/2010/07/21/throttling-function-calls Speaking of, I added the "wheel" event to the "scroll" event so that if you are at the top/bottom of the scroll the mouse wheel can also fire the scroll when no scroll actually occurs. I did not address other possible challenges such as the down/up arrow when the scroll is at the top/bottom; I will leave that up to you to address based upon your needs.
Note that upon a "search" event when typing, I reset the currentSearch list.
I left some console.log in place which you can remove - but allows you to see the page and some event fire logging.
Here is a sample so you can try this all out https://jsfiddle.net/MarkSchultheiss/hgfhh2y7/3/
var myApp = myApp || {};
myApp.data = {
currentSearch: [],
pageStart: 0,
pageEnd: 0,ma
perPage: 3,
page: 0,
lastScroll: 0,
scrollDelay: 250,
outputContainer: $('#inventory'),
excludes: ['Operation Phoenix Case Key', 'CS:GO Case Key', 'Winter Offensive Case Key', 'Revolver Case Key', 'Operation Vanguard Case Key', 'Operation Wildfire Case Key', 'Shadow Case Key', 'Operation Breakout Case Key', 'Chroma Case Key', 'Huntsman Case Key', 'Falchion Case Key', 'Chroma 2 Case Key']
};
myApp.func = {
contains: function(myArray, searchTerm, property) {
var found = [];
var len = myArray.length;
for (var i = 0; i < len; i++) {
if (myArray[i][property].toLowerCase().indexOf(searchTerm.toLowerCase()) > -1) found.push(myArray[i]);
}
return found;
},
paginate: function(items) {
myApp.data.pageStart = myApp.data.perPage * myApp.data.page;
myApp.data.pageEnd = myApp.data.pageStart + myApp.data.perPage;
if (myApp.data.pageEnd > items.length) {
myApp.data.pageEnd = items.length;
myApp.data.pageStart = myApp.data.pageEnd - myApp.data.perPage >= 0 ? myApp.data.pageEnd - myApp.data.perPage : 0;
}
console.log("Page:" + myApp.data.page + " Start:" + myApp.data.pageStart + " End:" + myApp.data.pageEnd + " max:" + items.length);
return items;
},
debounce: function(fn, delay) {
var timer = null;
return function() {
var context = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
};
},
throttle: function(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function() {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function() {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
}
},
renderItems: function(pageItems) {
// $("#inventory").html("");
console.log('renderStart Items:' + pageItems.length);
console.log(myApp.data.pageStart + ":" + myApp.data.pageEnd);
var renderList = pageItems.filter(function(itemValue) {
return !!(myApp.data.excludes.indexOf(itemValue) == -1)
}).slice(myApp.data.pageStart, myApp.data.pageEnd);
console.log(renderList);
var newContent = "";
renderList.forEach(function(item, index, arr) {
var message = 'BitSkins Price: $' + Math.round((item.bprice * 1));
if (item && item.price !== null) {
if (item.bprice === '') {
message = 'Item never sold on BitSkins!';
}
if (myApp.data.excludes.indexOf(item.name) == -1) {
newContent += "<li class='col 2 zoomIn animated'><div class='card item-card waves-effect waves-light first-style-thing' id='" + item.id + "'><div class='iteam second-style-thing' >" + item.name + "</div><div class='condition third-style-thing'>" + item.condition + "</div><div class='center-align fourth-style-thing' ><img title='" + item.originalname + "' draggable='false' src='https://steamcommunity-a.akamaihd.net/economy/image/" + item.iconurl + "/235fx235f'></div><div class='secondarea fifth-style-thing'><div class='buyer-price center-align sixth-style-thing'>$" + Math.round(item.price) + "<div class='bitskinscomp seventh-style-thing'>" + message + "</div></div><a class='btn waves-effect waves-light eighth-style-thing' href='" + item.inspect + "' target='_blank'>Inspect</a><a class='btn waves-effect waves-light purchaseButton'>Cart</a></div></li>";
}
}
myApp.data.outputContainer.html(newContent);
});
}
};
var items = [{
id: "123",
name: "freddy Beer",
condition: "worn",
originalname: "beer stein",
price: 10.22,
bprice: "34.33",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}, {
id: "123",
name: "freddy Beer",
condition: "worn",
originalname: "beer stein",
price: 10.22,
bprice: "34.33",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}, {
id: "123",
name: "freddy Beer",
condition: "worn",
originalname: "beer stein",
price: 10.22,
bprice: "34.33",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}, {
id: "123",
name: "freddy Beer",
condition: "worn",
originalname: "beer stein",
price: 10.22,
bprice: "34.33",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}, {
id: "123",
name: "Operation Phoenix Case Key",
condition: "worn",
originalname: "Operation Phoenix Case Key",
price: 10.22,
bprice: "34.33",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}, {
id: "234",
name: "Johnson Wax",
condition: "waxy",
originalname: "Ear wax",
price: 2244.22,
bprice: "",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}, {
id: "45245",
name: "Door Knob | Green",
condition: "green tint",
originalname: "Green door knob",
price: 35.68,
bprice: "",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXU5A1PIYQNqhpOSV-fRPasw8rsQEl9Jg9SpIW1KgRrg6GGJWRBtI-ykYTak6WhN76JlWgFsJN1j72SotWiigbi-0BqYjuncdDDdRh-Pw9UqwY-SA",
inspect: "http://example.com/myinspect/4"
}, {
id: "45245red",
name: "Door Knob | Red",
condition: "red tint",
originalname: "Red door knob",
price: 35.68,
bprice: "",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXU5A1PIYQNqhpOSV-fRPasw8rsQEl9Jg9SpIW1KgRrg6GGJWRBtI-ykYTak6WhN76JlWgFsJN1j72SotWiigbi-0BqYjuncdDDdRh-Pw9UqwY-SA",
inspect: "http://example.com/myinspect/4"
}, {
id: "45245red",
name: "Door Knob | Red",
condition: "red tint",
originalname: "Red door knob",
price: 35.68,
bprice: "",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXU5A1PIYQNqhpOSV-fRPasw8rsQEl9Jg9SpIW1KgRrg6GGJWRBtI-ykYTak6WhN76JlWgFsJN1j72SotWiigbi-0BqYjuncdDDdRh-Pw9UqwY-SA",
inspect: "http://example.com/myinspect/4"
}, {
id: "45245blue",
name: "Door Knob | Blue",
condition: "blue tint",
originalname: "Blue door knob",
price: 35.68,
bprice: "",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXU5A1PIYQNqhpOSV-fRPasw8rsQEl9Jg9SpIW1KgRrg6GGJWRBtI-ykYTak6WhN76JlWgFsJN1j72SotWiigbi-0BqYjuncdDDdRh-Pw9UqwY-SA",
inspect: "http://example.com/myinspect/4"
}, {
id: "45245Brown",
name: "Door Knob | Brown",
condition: "brown tint",
originalname: "Brown door knob",
price: 35.68,
bprice: "34.23",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXU5A1PIYQNqhpOSV-fRPasw8rsQEl9Jg9SpIW1KgRrg6GGJWRBtI-ykYTak6WhN76JlWgFsJN1j72SotWiigbi-0BqYjuncdDDdRh-Pw9UqwY-SA",
inspect: "http://example.com/myinspect/4"
}, {
id: "45245Malt",
name: "Beer malt | Brown",
condition: "brown tint",
originalname: "Brown Beer Malt ",
price: 35.68,
bprice: "34.23",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}, {
id: "4Beef",
name: "Beefeaters Mug | Brown",
condition: "new tint",
originalname: "Brown Beefeaters mug",
price: 35.68,
bprice: "34.23",
iconurl: "-9a81dlWLwJ2UUGcVs_nsVtzdOEdtWwKGZZLQHTxDZ7I56KU0Zwwo4NUX4oFJZEHLbXH5ApeO4YmlhxYQknCRvCo04DEVlxkKgpot7HxfDhjxszJemkV08u_mpSOhcjnI7TDglRc7cF4n-T--Y3nj1H6-hBrMW_3LIOWdlU_MlGDqwO6wrvq15C6vp-bnHY36SAm4XbYl0SwhgYMMLJqUag1Og",
inspect: "http://example.com/myinspect/4"
}];
myApp.data.outputContainer.on('customRenderEvent', function() {
myApp.func.renderItems(myApp.func.paginate(myApp.data.currentSearch));
});
$('#SearchItemsFromList').on('keyup', function() {
var valThis = $(this).val();
if (valThis === "") {
// item-card
// items hold the things to pageinate
// currentSearch holds the filtered items
myApp.data.currentSearch = items;
} else {
// "name" is the matching property in the object
myApp.data.currentSearch = myApp.func.contains(items, valThis, "name");
}
myApp.data.outputContainer.trigger('customRenderEvent');
console.log("keyup len:" + myApp.data.currentSearch.length);
}).trigger('keyup'); // trigger for initial display
$(window).on('scroll wheel', myApp.func.debounce(function(event) {
// set the page on scroll up/down
if ($(this).scrollTop() == 0) {
myApp.data.page > 0 ? myApp.data.page-- : myApp.data.page = 0;
} else {
myApp.data.page++;
}
myApp.func.renderItems(myApp.func.paginate(myApp.data.currentSearch));
}, myApp.data.scrollDelay));
Final note on the code, you have a quite long, difficult to maintain conditional which I replaced by added an array with the exclusions and then the code uses it with a filter: .filter(function(itemValue) {
return !!(myApp.data.excludes.indexOf(itemValue) == -1)
})

Related

How to use javascript to achieve scroll loading, the mouse can automatically scroll to the bottom to display the newly loaded content?

I want to realize that when scrolling to the bottom of the window, more information can be loaded in, so far it has been done! But I have two problems: 1. I don't know how to make the scroll scroll to the bottom to see the newly loaded data when the data is loaded? 2. Since I will trigger a loading function when I scroll to the bottom, if it will automatically scroll to the bottom when the data is loaded, this will not trigger the loading function. What I want is to only load each time I scroll Enter four data. I have tried many ways and still don't know how to solve the above two problems I encountered, I hope to get your help, thank you very much.
let str = ""
let limit = 4;
let offset = 0;
let data = [{
id: 5,
title: "title5"
},
{
id: 6,
title: "title6"
},
{
id: 7,
title: "title7"
},
{
id: 8,
title: "title8"
},
{
id: 9,
title: "title9"
},
{
id: 10,
title: "title10"
},
{
id: 11,
title: "title11"
},
{
id: 12,
title: "title12"
},
{
id: 13,
title: "title13"
},
{
id: 14,
title: "title14"
},
{
id: 15,
title: "title15"
}
];
function addData() {
let newData = data.splice(0, limit);
if (newData.length === 0) return;
str = "";
newData.forEach(e => {
str += ` <li class="message">
<div class='id'>${e.id}</div>
<div>${e.title}</div>
</li>`
});
content.innerHTML += str;
}
let loading = false;
let content = document.querySelector('.content');
content.addEventListener('scroll', function() {
if (loading || data.length === 0) return;
if (content.scrollTop + content.clientHeight >= content.scrollHeight) {
loading = true;
document.querySelector('.loading').style.display = 'block';
setTimeout(() => {
addData();
document.querySelector('.loading').style.display = 'none';
loading = false;
}, 1000);
}
});
.content {
width: 600px;
height: 200px;
overflow: auto;
border: 1px solid;
}
.message {
display: flex;
padding: 30px;
border: 1px solid #ddd;
}
.id {
margin-right: 8px;
}
.loading {
display: none;
width: 60px;
height: 60px;
margin: 0 auto;
background: url('https://media.giphy.com/media/3o7bu3XilJ5BOiSGic/giphy.gif');
}
<ul class="content">
<li class="message">
<div class='id'>1</div>
<div>title1</div>
</li>
<li class="message">
<div class='id'>2</div>
<div>title2</div>
</li>
<li class="message">
<div class='id'>3</div>
<div>title3</div>
</li>
<li class="message">
<div class='id'>4</div>
<div>title4</div>
</li>
<div class="loading"></div>
</ul>

jQuery UI Droppable items

Hello
I have a problem. The problem is, when I move an item to multiple slots it works perfectly.
But when i have two items of the same type, and I put them together it turns into a single item with the value of 2.
But I can't figure out how i could separate that two items into two separate items with the value of one.
if($(this)[0].querySelector('.size').innerText > 1) {
$(this).children().children().html(1);
}
Project: https://codepen.io/KsenonAdv/pen/bGRaRjR
Consider the following example.
$(function() {
function log(string) {
console.log(Date.now(), string);
}
function makeGrid() {
log("Make Grid...");
for (var i = 0; i < 36; i++) {
$("<div>", {
class: "slot"
}).data({
count: 0,
stackable: true
}).appendTo(".slots");
}
log("-* Grid Complete *-");
}
function makeDraggable(target) {
log("Make Drag, Current Class: " + $(target).attr("class"));
console.log(target);
$(target).draggable({
scroll: false,
helper: "clone",
cursor: "pointer",
zIndex: 27,
revert: "invalid"
});
log("After Make Drag, Current Class: " + $(target).attr("class"));
console.log(target);
}
function refreshSlot(target) {
log("Refresh Slot");
$(".size", target).html($(target).data("count"));
}
function addItem(slot, item) {
log("Add Item " + item.id);
$.extend(item, {
stackable: (item.category != 0),
count: (item.count == undefined ? 1 : item.count)
});
slot = $(slot);
var newItem = $("<div>", {
class: "item",
id: item.id,
})
.data(item)
.css("background-image", "url(" + item.img + ")");
newItem.appendTo(slot);
$("<div>", {
class: "size"
}).appendTo(slot);
slot.data({
count: item.count,
stackable: item.stackable
});
log("Item Added - Refreshing");
refreshSlot(slot);
}
function emptySlot(target) {
log("Empty Slot " + $(target).index());
$(target).data({
count: 0,
stackable: true
}).empty();
}
function loadPlayerItems(items) {
log("Loading Player Items...");
$.each(items, function(index, item) {
addItem($(".slots > .slot").eq(index), item);
});
log("-* Loading Complete *-");
}
function isAcceptable(item) {
if ($(this).children().length == 0 || $(".item", this).data("stackable")) {
return true;
}
return false;
}
var pItems = {
playerItems: [{
img: 'https://i.imgur.com/5cjSI9X.png',
name: 'bat',
id: 1,
category: 0
},
{
img: 'https://i.imgur.com/HLQORBk.png',
name: 'axe',
id: 2,
category: 1
},
{
img: 'https://i.imgur.com/HLQORBk.png',
name: 'axe',
id: 3,
category: 1
}
]
};
makeGrid();
loadPlayerItems(pItems.playerItems);
makeDraggable(".item");
$(".slots > .slot").droppable({
accept: isAcceptable,
drop: function(event, ui) {
var origin = ui.draggable.parent();
var target = $(this);
log("Drop Accepted; From: " + origin.index() + ", To: " + target.index());
// Increase the Count
target.data("count", target.data("count") + 1);
// Check for Stack
if (target.children().length == 0) {
addItem(target, ui.draggable.data());
} else {
refreshSlot(target);
}
// Check Orginal Stack
if (origin.data("count") > 1) {
origin.data("count", origin.data("count") - 1);
refreshSlot(origin);
} else {
emptySlot(origin);
}
makeDraggable($(".item", this));
}
});
});
body {
display: flex;
justify-content: space-between;
background: url(http://img.playground.ru/images/5/1/GTA5_2015-05-07_00-53-36-07.png);
}
#inventory {
height: 56.25vw;
background: rgba(0, 0, 0, .5);
}
.list-slots {
position: relative;
background: rgba(0, 0, 0, .7);
max-width: 80%;
}
.slots {
max-width: 87%;
margin: auto;
padding-top: 3.125vw;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.slots .slot {
width: 100px;
height: calc(100px + 1em);
background-color: #ccc;
margin-bottom: 0.781vw;
}
.slots .slot .item {
background-repeat: no-repeat;
width: 100px;
height: 100px;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div id="inventory">
<div class="list-slots">
<div class="slots"></div>
</div>
</div>
As discussed in the comment, this updates the Count of items in the Slot by 1. When an items is dragged out, the count is lowered by 1.
To make things easier, I created a number of functions. You may choose to handle it in another manner if you like.

how to dynamically resize text in a vue component

I have built an application with different widgets you can drop into a dashboard. Each widget contains a component that a user would like to see (kind of like grafana if you've ever seen it).
Question: When the user drags the grid-item to increase or decrease the size, how do you update the html inside of my component to adjust to the size of the new item?
What I've tried:
attempted to wrap the p tags in an SVG and use viewport.
attempted to change my size to VW to dynamically scale by the viewport but the viewport is not of the component but of the entire spa.
I attempted to get the parent size using this.$parent and did some math to get the text size and dynamically assign it to a component but this was very messy. Also, the sizes displayed were not right.
Below is my code for my grid using the vue-grid-layout package
<grid-layout
ref="widgetGrid"
:layout.sync="widgets"
:col-num="12"
:row-height="verticalSize"
:is-draggable="editable"
:is-resizable="editable"
:is-mirrored="false"
:responsive="true"
:autoSize="editable"
:prevent-collision="false"
:vertical-compact="false"
:margin="[10, 10]"
:use-css-transforms="true"
#layout-updated="layoutUpdatedEvent"
>
<grid-item
:ref="`widget_${widget.i}`"
v-for="widget in widgets"
:key="widget.i"
:x="widget.x"
:y="widget.y"
:w="widget.w"
:h="widget.h"
:i="widget.i"
:static="!editable"
>
<template>
<component
:is="widget.WidgetType"
:setup="false"
:widgetConfig="widget.WidgetConfig"
></component>
</template>
</grid-item>
</grid-layout>
My component that I'm trying to resize the text for is below. It's a vue file and I've included styling.
<template>
<div>
<div id="clock">
<p class="date">{{ date }}</p>
<p class="time">{{ time }}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
time: '',
date: '',
week: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
ticker: null
};
},
created() {
this.ticker = setInterval(this.updateTime, 1000);
},
mounted() {
this.showDate =
this.widgetConfig?.Settings?.find((x) => x.Key === 'ShowDate').Value ||
false;
this.showTime =
this.widgetConfig?.Settings?.find((x) => x.Key === 'ShowTime').Value ||
false;
},
methods: {
updateTime() {
let cd = new Date();
this.time =
this.zeroPadding(cd.getHours(), 2) +
':' +
this.zeroPadding(cd.getMinutes(), 2) +
':' +
this.zeroPadding(cd.getSeconds(), 2);
this.date =
this.zeroPadding(cd.getFullYear(), 4) +
'-' +
this.zeroPadding(cd.getMonth() + 1, 2) +
'-' +
this.zeroPadding(cd.getDate(), 2) +
' ' +
this.week[cd.getDay()];
},
zeroPadding(num, digit) {
let zero = '';
for (let i = 0; i < digit; i++) {
zero += '0';
}
return (zero + num).slice(-digit);
}
},
};
</script>
<style lang="scss" scoped>
html,
body {
height: 100%;
}
body {
background: #0f3854!important;
background: radial-gradient(ellipse at center, #0a2e38 0%, #000000 70%)!important;
background-size: 100%;
}
p {
margin: 0;
padding: 0;
}
#clock {
font-family: 'Share Tech Mono', monospace;
color: #ffffff;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: #daf6ff;
text-shadow: 0 0 20px rgba(10, 175, 230, 1), 0 0 20px rgba(10, 175, 230, 0);
.time {
letter-spacing: 0.05em;
font-size: 1vw;
padding: 5px 0;
}
.date {
letter-spacing: 0.1em;
font-size: 1vw;
}
.text {
letter-spacing: 0.1em;
font-size: 1vw;
padding: 20px 0 0;
}
}
</style>
Nuxt Config
module.exports = {
head: {
titleTemplate: '',
title: 'QVue',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'QVue Web UI' },
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
},
css: [],
plugins: [
{ src: '#/plugins/vueGrid', ssr: false }
],
publicRuntimeConfig: {},
privateRuntimeConfig: {},
components: true,
buildModules: [
'#nuxtjs/vuetify'
],
modules: [
'#nuxtjs/axios'
],
axios: {
},
build: {
},
render: {
compressor: false,
},
srcDir: 'client/',
};
If you'd like to resize the text size for your clock component, one solution is uses svg -> viewbox the one you already mentioned. But you need to use <text> instead of <p>.
Below is the demo:
Vue.component('v-clock',{
template:`
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<text x="0" y="25" fill="red">{{date}}</text>
<text x="0" y="75" fill="red">{{time}}</text>
</svg>
`,
data() {
return {
time: '',
date: '',
week: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
ticker: null
};
},
created() {
this.ticker = setInterval(this.updateTime, 1000);
},
mounted() {
this.showDate =
this.widgetConfig?.Settings?.find((x) => x.Key === 'ShowDate').Value ||
false;
this.showTime =
this.widgetConfig?.Settings?.find((x) => x.Key === 'ShowTime').Value ||
false;
},
methods: {
updateTime() {
let cd = new Date();
this.time =
this.zeroPadding(cd.getHours(), 2) +
':' +
this.zeroPadding(cd.getMinutes(), 2) +
':' +
this.zeroPadding(cd.getSeconds(), 2);
this.date =
this.zeroPadding(cd.getFullYear(), 4) +
'-' +
this.zeroPadding(cd.getMonth() + 1, 2) +
'-' +
this.zeroPadding(cd.getDate(), 2) +
' ' +
this.week[cd.getDay()];
},
zeroPadding(num, digit) {
let zero = '';
for (let i = 0; i < digit; i++) {
zero += '0';
}
return (zero + num).slice(-digit);
}
}
})
new Vue ({
el:'#app',
data () {
return {
size: 100
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<input v-model.number="size" type="range" min="50" max="500"/>
<div :style="{width: size + 'px', height: size + 'px'}" style="border: solid 1px blue">
<v-clock></v-clock>
</div>
</div>

Display Array Data in Table

I have a div with id="result", I want to show my data in this div tag.
How to append this table data.
I did not understand in table data tag table is in the string so how it works
function displayAddress() {
if (flag == 0) {
tabledata = "<table style='position: fixed; background-color:lightgrey; border: 1px solid black; border-collapse: collapse; margin-top: 25px;' border = '1'><tr><th>Name</th><th>Type</th><th>Address</th><th>Email</th><th>Mobile</th><th>Location</th></tr>";
}
for (i = 0; i < dataArray.length; i++) {
var tempname = dataArray[i].Name;
var temptype = dataArray[i].Type;
var tempaddress = dataArray[i].Address;
var tempemail = dataArray[i].Email;
var tempmobile = dataArray[i].Mobile;
var templocation = dataArray[i].Location;
//Please fill the required code to store the values in tabledata.
}
console.log(tabledata);
if (flag == 0) {
//Please fill the required code to store the table data in result.
document.getElementById("name").value = "";
document.getElementsByName("type").checked = false;
document.getElementById("address").value = "";
document.getElementById("email").value = "";
document.getElementById("mobile").value = "";
document.getElementById("location").value = "";
}
count = 0;
}
This is one way
const dataArray = [{
"Name": "Joe",
"Type": "Contractor",
"Address": "Address 1",
"Email": "Email#email.com",
"Mobile": "0123456789",
"Location": "At home"
},
{
"Name": "Jane",
"Type": "Contractor",
"Address": "Address 2",
"Email": "Email#email.com",
"Mobile": "1234567890",
"Location": "At home"
}
];
const tb = document.getElementById("tb");
let tr = [];
dataArray.forEach(item => {
tr.push('<tr><td>' + item.Name + '</td>')
tr.push('<td>' + item.Type + '</td>')
tr.push('<td>' + item.Address + '</td>')
tr.push('<td>' + item.Email + '</td>')
tr.push('<td>' + item.Mobile + '</td>')
tr.push('<td>' + item.Location + '</td></tr>')
})
tb.innerHTML = tr.join("");
document.getElementById("result").classList.remove("hide"); // show
#table1 {
position: fixed;
background-color: lightgrey;
border: 1px solid black;
border-collapse: collapse;
margin-top: 25px;
}
#table1 tr td {
border: 1px solid black;
}
.hide { display:none; }
<div id="result" class="hide">
<table id="table1">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Address</th>
<th>Email</th>
<th>Mobile</th>
<th>Location</th>
</tr>
</thead>
<tbody id="tb"></tbody>
</table>
</div>
Alternative method using template literals
dataArray.forEach(item => {
tr.push(`<tr>
<td>${item.Name}</td>
<td>${item.Type}</td>
<td>${item.Address}</td>
<td>${item.Email}</td>
<td>${item.Mobile}</td>
<td>${item.Location}</td>
</tr>`);
})
for mysself I do that [very shorter] way.
All the documentation is here -> https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement
const resultDiv = document.getElementById('result')
, myTable = resultDiv.appendChild(document.createElement('table')) // create the TABLE
, rowList =
[ { lib:'Names', key: 'Name' }, { lib:'Types', key:'Type' }
, { lib:'Address', key: 'Address' }, { lib:'eMails', key:'Email' }
, { lib:'phone', key: 'Mobile' }, { lib:'Locations', key:'Location' }
];
// sample data
const dataArray =
[ { Name: "Joe", Type: "Contractor", Address: "Address 1", Email: "Email_1#email.com", Mobile: "0123456789", Location: "At home"}
, { Name: "Jane", Type: "Contractor", Address: "Address 2", Email: "Email_2#email.com", Mobile: "0987654321", Location: "someWhere"}
]
;
dataArray.forEach(item=>
{
let newRow = myTable.insertRow()
rowList.forEach(rowName=>newRow.insertCell().textContent = item[rowName.key])
})
// make header part at last
let theHeader = myTable.createTHead().insertRow()
rowList.forEach(rowName=>theHeader.insertCell().textContent = rowName.lib)
/* cosmetic part */
table {
border-collapse: collapse;
margin-top: 25px;
font-family: Arial, Helvetica, sans-serif;
font-size: 10px;
}
thead {
background-color: aquamarine;
}
tbody {
background-color: #b4c5d8;
}
td {
border: 1px solid grey;
padding: .3em .7em;
}
<div id="result"></div>
there are two ways, the easy version is
document.getElementById("element").innerHTML = yourArray[0]
document.getElementById("element").innerHTML = yourArray[1]
document.getElementById("element").innerHTML = yourArray[2]
...
second way is map() method
yourArray.map(item => {
elements.innerHTML = item
});
if you use a framework (like Vue, React) second way is more easier
dont work try this
var email = yourArray[0]
var password = yourArray[1]
var table = "<table><tr> <td>" + email + "</td><td>" + password "</td></tr></table>

Looping through an array of objects to display information

I am on a Team Treehouse challenge.
My challenge is to create an array of objects which contains the students info, make a search database for it so when you search for the student, the student will appear.
The additional challenge is to print out the both students if there is 2 students of same name. I also tried adding another little challenge to list all students if user typed "list".
My code:
var message = '';
var student;
var search;
var list;
function print(message) {
var outputDiv = document.getElementById('output');
outputDiv.innerHTML = message;
}
function getStudentInfo (student) {
var report = '<h2>Student: ' + student.name + '</h2>';
report += '<p>Track: ' + student.track + '</p>';
report += '<p>Points: ' + student.points + '</p>';
report += '<p>Achievements: ' + student.achievements + '</p>';
return report;
}
while (true) {
search = prompt("Search Student records: Type a name [Martin] or [quit] to exit");
if (search === null || search.toLowerCase() === "quit") {
break;
}
for (var i = 0; i < students.length; i += 1) {
student = students [i];
if (search.toLowerCase() === student.name.toLowerCase()) {
message = getStudentInfo(student);
print(message);
break;
} else if (search.toLowerCase() === "list") {
list = '<h2>Student: ' + student.name + '</h2>';
list += '<p>Track: ' + student.track + '</p>';
list += '<p>Points: ' + student.points + '</p>';
list += '<p>Achievements: ' + student.achievements + '</p>';
print (list);
}
}
}
var students = [
{
name: 'Dave',
track: 'Front End Development',
achievements: 158,
points: 14730
},
{
name: 'Jody',
track: 'iOS Development with Swift',
achievements: '175',
points: '16375'
},
{
name: 'Jordan',
track: 'PHP Development',
achievements: '55',
points: '2025'
},
{
name: 'John',
track: 'Learn WordPress',
achievements: '40',
points: '1950'
},
{
name: 'Jordan',
track: 'Rails Development',
achievements: '5',
points: '350'
}
];
A couple of notable things:
This is poor use of a while loop and using while (true) tends to be error-prone leading to infinite loops. The point of having a prompt in a loop would be to continually expect additional input from the user, but since you break; out of every loop I don't believe that is your goal. You actually don't need a while construct at all.
You don't need so many variables to keep track of the program's state. It's fine to repeat students[i] twice rather than making a new variable for it, and you can store the user's input as toLowerCase() right away since that's all you'll ever be checking.
Unless multiple places in your code need to output to the UI, you don't need a separate function for outputting the message. You can just build up the message and output it at the end of the script. My hunch is that this build-up of the final output is your biggest point of confusion, and the extra function call adds to that.
Overall though, it looks like you were pretty close - the following code is actually shorter than what you had and mainly changes how the output message is built.
(function() {
var students = [
{
name: 'Dave',
track: 'Front End Development',
achievements: 158,
points: 14730
},
{
name: 'Jody',
track: 'iOS Development with Swift',
achievements: '175',
points: '16375'
},
{
name: 'Jordan',
track: 'PHP Development',
achievements: '55',
points: '2025'
},
{
name: 'John',
track: 'Learn WordPress',
achievements: '40',
points: '1950'
},
{
name: 'Jordan',
track: 'Rails Development',
achievements: '5',
points: '350'
}
];
var i = 0;
var input = '';
var output = '';
function getStudentInfo (student) {
var report = '<h2>Student: ' + student.name + '</h2>';
report += '<p>Track: ' + student.track + '</p>';
report += '<p>Points: ' + student.points + '</p>';
report += '<p>Achievements: ' + student.achievements + '</p>';
return report;
}
input = prompt("Search Student records: Type a name [Martin] or [quit] to exit").toLowerCase();
if (input === 'quit' || input.length === 0) {
return; // or display some message to 'output'
}
for (i = 0; i < students.length; i++) {
if (input === 'list') {
output += getStudentInfo(students[i]);
} else if (input === students[i].name.toLowerCase()) {
output += getStudentInfo(students[i]);
}
}
document.getElementById('output').innerHTML = output;
})();
#import url('http://necolas.github.io/normalize.css/3.0.2/normalize.css');
/*General*/
body {
background: #fff;
max-width: 980px;
margin: 0 auto;
padding: 0 20px;
font: Helvetica Neue, Helvectica, Arial, serif;
font-weight: 300;
font-size: 1em;
line-height: 1.5em;
color: #8d9aa5;
}
a {
color: #3f8aBf;
text-decoration: none;
}
a:hover {
color: #3079ab;
}
a:visited {
color: #5a6772;
}
h1, h2, h3 {
font-weight: 500;
color: #384047;
}
h1 {
font-size: 1.8em;
margin: 60px 0 40px;
}
h2 {
font-size: 1em;
font-weight: 300;
margin: 0;
padding: 30px 0 10px 0;
}
#home h2 {
margin: -40px 0 0;
}
h3 {
font-size: .9em;
font-weight: 300;
margin: 0;
padding: 0;
}
h3 em {
font-style: normal;
font-weight: 300;
margin: 0 0 10px 5px;
padding: 0;
color: #8d9aa5;
}
ol {
margin: 0 0 20px 32px;
padding: 0;
}
#home ol {
list-style-type: none;
margin: 0 0 40px 0;
}
li {
padding: 8px 0;
display: list-item;
width: 100%;
margin: 0;
counter-increment: step-counter;
}
#home li::before {
content: counter(step-counter);
font-size: .65em;
color: #fff;
font-weight: 300;
padding: 2px 6px;
margin: 0 18px 0 0;
border-radius: 3px;
background:#8d9aa5;
line-height: 1em;
}
.lens {
display: inline-block;
width: 0;
height: 0;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 8px solid #8d9aa5;
border-radius: 5px;
position: absolute;
margin: 5px 0 0 -19px;
}
#color div {
width: 50px;
height: 50px;
display: inline-block;
border-radius: 50%;
margin: 5px;
}
span {
color: red;
}
<h1>Students</h1>
<div id="output"></div>

Categories