I have a question about jQuery: I now render content from an array to the dom with:
errors.forEach(error => {
let message = error.message;
let fileName = error.fileName;
$('.errors').append("<li class=\"error-item\">".concat(fileName, " : ").concat(message, "</li><br>"));
});
but I got some actions that update the array where I render the content from, but when I run this function after the update of the array, it appends. I use the .empty() before now but that feels like a hack around but I want it to update
I hope this make sense
What can I do that it updates the dom instead of appending?
I hope someone can help me with this
The jQuery method .html() overwrites the content of the targeted element(s) with a given htmlString. The following demo function logger() will accept an array of objects and render the data in a <ul>.
let failData = [{
DOM: '.fail',
title: 'Errors: ',
header: ['File', 'Message']
},
{
item: '12ffda8a99.log',
message: 'Connection failed'
},
{
item: 'bb6200c400.log',
message: 'Corrupted download'
},
{
item: 'd93ba66731.log',
message: 'Encryption key needed'
},
{
item: '08caa5240f.log',
message: 'Mismatched certification'
},
{
item: 'dead0b8a99.log',
message: 'Insecure protocol'
}
];
let warnData = [{
DOM: '.warn',
title: 'Alerts: ',
header: ['System', 'Message']
},
{
item: 'network',
message: '1GB of data left before limit is exceeded'
},
{
item: 'file',
message: '1GB of storage left before partition is full'
},
{
item: 'power',
message: '5% of battery remains'
}
];
let infoData = [{
DOM: '.info',
title: 'Updates: ',
header: ['Priority', 'Message']
},
{
item: 'critical',
message: 'Anti-virus update required'
},
{
item: 'optional',
message: 'Media encoding update available'
}
];
const logger = array => {
let html = '';
let list;
for (let [index, object] of array.entries()) {
list = $(array[0].DOM);
if (index === 0) {
list.prev('header').html(`<u><b>${object.title}</b><output>${array.length-1}</output></u><u><b>${object.header[0]}</b><b>${object.header[1]}</b></u>`);
} else {
html += `<li><b>${object.item}</b><b>${object.message}</b></li>`;
}
}
list.html(html);
return false;
}
logger(failData);
logger(warnData);
logger(infoData);
header {
margin: 10px 0 -15px;
}
ul {
padding: 5px 0 10px;
border: 3px ridge #777;
overflow-y: hidden;
}
ul,
header {
display: table;
table-layout: fixed;
width: 90%;
}
li,
u {
display: table-row;
}
b {
display: table-cell;
width: 50%;
border-bottom: 1px solid #000;
padding-left:5px
}
output {
display: inline-block;
width: 10ch;
}
<main>
<section class='logs'>
<header></header>
<ul class='fail'></ul>
<header></header>
<ul class='warn'></ul>
<header></header>
<ul class='info'></ul>
</section>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Related
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>
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 do I create a dynamical filter using a computed property from vue when the dataset is from a graphql-query?
I've looked at several articles that all use the array.filter()-method, but I can't get it to work on my dataset (dummy data below):
books: [{
node: {
title: 'Elon Musk',
by:'Ashlee Vance',
},
node: {
title: 'Steve Jobs',
by:'George Llian',
},
node: {
title: 'Face of Facebook',
by: 'Sandip Paul',
},
node: {
title: 'Tim Cook',
by:'Andy Atkins',
url:'http://www.voidcanvas.com/'
},
node: {
title: 'Abdul Kalam',
by:'Arun Tiwari',
},
node: {
title: 'Story of Elon Musk',
by:'BP John',
},
node: {
title: 'Story of Bill Gates',
by:'Russel Crook',
},
node: {
title: 'Becoming Steve Jobs',
by:'Andrew Russel',
}
}]
Method:
computed: {
filteredBooks: function () {
var books_array = this.books,
searchString = this.searchString;
if(!searchString) {
return books_array;
}
searchString = searchString.trim().toLowerCase();
books_array = books_array.filter(function(item) {
if(item.node.title.toLowerCase().indexOf(searchString) !== -1) {
return item;
}
});
return books_array;
}
HTML:
<div id="app">
<input type="text" v-model="searchString" placeholder="search" />
<ul style="list-style: none;">
<li v-for="book in filteredBooks">
<p>{{book.title}} -by- {{book.by}}</p>
</li>
</ul>
</div>
This is my first coding project since early 2000, so please feel free to point me in the right direction if this is the wrong forum for this question.
I set up a jsfiddle to play with the case.
Here is the code with some modifications:
var app = new Vue({
el: '#app',
data: {
searchString: '',
books: [{
title: 'Elon Musk',
by: 'Ashlee Vance'
},
{
title: 'Steve Jobs',
by: 'George Llian'
},
{
title: 'Face of Facebook',
by: 'Sandip Paul'
},
{
title: 'Tim Cook',
by: 'Andy Atkins',
url: 'http://www.voidcanvas.com/'
},
{
title: 'Abdul Kalam',
by: 'Arun Tiwari'
},
{
title: 'Story of Elon Musk',
by: 'BP John'
},
{
title: 'Story of Bill Gates',
by: 'Russel Crook'
},
{
title: 'Becoming Steve Jobs',
by: 'Andrew Russel'
}
]
},
computed: {
filteredBooks: function() {
return this.books.filter(e => this.searchString === '' ? false : e.title.toLowerCase().indexOf(this.searchString.toLowerCase()) !== -1 ? true : false);
}
}
});
body {
background-color: #dbd8d8;
padding: 20px;
}
input {
width: 300px;
height: 30px;
padding: 0.2rem;
}
.design {}
p {
position: relative;
display: block;
padding: .4em .4em .4em 2em;
margin: .5em 0;
border: 3px solid white;
background: #FC756F;
color: #444;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="searchString" placeholder="search" />
<ul style="list-style: none;">
<li v-for="book in filteredBooks">
<p>{{book.title}} -by- {{book.by}}</p>
</li>
</ul>
</div>
Remove the node: from before the objects in the books data array - books array should contain a bunch of plain objects. If you put node: before each object, then you "say" that the every node is the key of key-value pair of an object (so the keynames will be identical - node!!!)
Simplify filteredBooks computed - no need to store all the variables. This function (filteredBooks) doesn't change the inputs, so you can use this here. The filter() functions doesn't change the array it filters - rather it returns a new array, containing only values that the iteratee function "saw" as true
You check for !searchString and that's never the case - searchString is always going to be true as you initialize it with searchString: '' (an empty value - but a value), so I changed it checking for the empty value in the filteredBooks computed.
I modified the code so that it compares lowercase to lowercase. With your code if someone typed a search string in uppercase, then there'd have been no match.
I have a Kendo TreeList where I search for the row where myDataItem is (with help of the uid or value).
When I execute:
$("#treeList tbody").on("click", "tr", function (e) {
var rSelectedRow = $(this);
var getSelect = treeList.select();
console.log("'real' selected row: "+rSelectedRow);
console.log("selected row: "+getSelect);
});
and in another function, where I want to get a row (not the selected one, just a row where myDataItem is):
var grid = treeList.element.find(".k-grid-content");
var myRow = grid.find("tr[data-uid='" + myDataItem.uid + "']"));
//or
// myRow = treeList.content.find("tr").eq(myDataItem.val);
console.log("my row:" + myRow)
I get:
'real' selected row: Object [ tr.k-alt ... ]
selected row: Object { length: 0 ... }
my row: Object { length: 0 ... }
I need the same structure of rSelectedRow for myRow. So how can I get the ,real' tr-element?
UPDATE: I wrote this method to find the row of my new added item:
//I have an id of the item and put it in an invisible row in the treelist.
getRowWhereItem: function (itemId) {
treeList.dataSource.read();
$("#menuItemTree .k-grid-content tr").each(function (i) {
var rowId = $(this).find('td:eq(1)').text();
console.log(itemId);
console.log(rowId);
if (rowId == itemId) {
console.log("found");
console.log(itemId + " " + rowId);
var row = this;
console.log(row);
return row;
}
});
},
The each-iteration reaches all tr's until/except the new added one. Why?
Use the change event instead of binding a click event to the widget's DOM. Note that for change to work, you need to set selectable to true:
<!-- Orginal demo at https://demos.telerik.com/kendo-ui/treelist/index -->
<!DOCTYPE html>
<html>
<head>
<base href="https://demos.telerik.com/kendo-ui/treelist/index">
<style>html { font-size: 14px; font-family: Arial, Helvetica, sans-serif; }</style>
<title></title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.3.911/styles/kendo.common-material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.3.911/styles/kendo.material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.3.911/styles/kendo.material.mobile.min.css" />
<script src="https://kendo.cdn.telerik.com/2018.3.911/js/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2018.3.911/js/kendo.all.min.js"></script>
</head>
<body>
<div id="example">
<div id="treelist"></div>
<script id="photo-template" type="text/x-kendo-template">
<div class='employee-photo'
style='background-image: url(../content/web/treelist/people/#:data.EmployeeID#.jpg);'></div>
<div class='employee-name'>#: FirstName #</div>
</script>
<script>
$(document).ready(function() {
var service = "https://demos.telerik.com/kendo-ui/service";
$("#treelist").kendoTreeList({
dataSource: {
transport: {
read: {
url: service + "/EmployeeDirectory/All",
dataType: "jsonp"
}
},
schema: {
model: {
id: "EmployeeID",
parentId: "ReportsTo",
fields: {
ReportsTo: { field: "ReportsTo", nullable: true },
EmployeeID: { field: "EmployeeId", type: "number" },
Extension: { field: "Extension", type: "number" }
},
expanded: true
}
}
},
height: 540,
filterable: true,
sortable: true,
columns: [
{ field: "FirstName", title: "First Name", width: 280,
template: $("#photo-template").html() },
{ field: "LastName", title: "Last Name", width: 160 },
{ field: "Position" },
{ field: "Phone", width: 200 },
{ field: "Extension", width: 140 },
{ field: "Address" }
],
pageable: {
pageSize: 15,
pageSizes: true
},
/* See here */
selectable: true,
change: function() {
let $selectedItem = this.select(),
dataItem1 = this.dataItem($selectedItem),
uid1 = $selectedItem.data("uid"),
uid2 = dataItem1.uid,
dataItem2 = this.dataSource.getByUid(uid1);
console.log("selected item", $selectedItem);
console.log("dataItem", dataItem1);
console.log("dataItem(alternative way)", dataItem2);
console.log("uid", uid1);
console.log("uid(alternative way)", uid2);
}
});
});
</script>
<style>
.employee-photo {
display: inline-block;
width: 32px;
height: 32px;
border-radius: 50%;
background-size: 32px 35px;
background-position: center center;
vertical-align: middle;
line-height: 32px;
box-shadow: inset 0 0 1px #999, inset 0 0 10px rgba(0,0,0,.2);
margin-left: 5px;
}
.employee-name {
display: inline-block;
vertical-align: middle;
line-height: 32px;
padding-left: 3px;
}
</style>
</div>
</body>
</html>
The part that really matters:
selectable: true,
change: function() {
let $selectedItem = this.select(),
dataItem1 = this.dataItem($selectedItem),
uid1 = $selectedItem.data("uid"),
uid2 = dataItem1.uid,
dataItem2 = this.dataSource.getByUid(uid1);
console.log("selected item", $selectedItem);
console.log("dataItem", dataItem1);
console.log("dataItem(alternative way)", dataItem2);
console.log("uid", uid1);
console.log("uid(alternative way)", uid2);
}
Demo
I didn't find a solution where I can get the tr by datauid.
But in my case, I took the id of the item and put it in an invisible row in the treelist.
Therefore, the method getRowWhereItem(itemId) in the question works well when making it execute after a successfull Ajax Call. With the callback from my ajax call, I load the new item and then the method can find the row. So the issue was that I had to have the uptodate data from my database.
Another issue was with contexts. The method getRowWhereItem(itemId) was a method of a namespace. I tried to call that method outside the namespace and couldn't get its return. Now, I moved the method into the same context where I call it.
(note: My development follows the MVC pattern, Administration is a Controller-class)
$.ajax({
url: General.createMethodUrl("Administration", "Admin", "Add_New"),
data: { parentItemId: parentOfNewId },
type: "POST",
dataType: "json",
success: function (response) {
if (response) {
var addedItem = $.parseJSON(response);
//waiting for callback because otherwise the window opens a few milliseconds before the properties are loaded and newRow cannot be find
tManag.ajaxCallSelectedEntry(addedItem.Id, treeList, function () {
newRow = tManag.getRowWhereItem(addedItem.Id);
});
jQuery(newRow).addClass("k-state-selected")
} else {
tManag.alertDialog(dialog, "Adding New Item Notification", response.responseText);
}
},
error: function (response) {
tManag.alertDialog(dialog, "Adding New Item Error", "Error");
}
});
http://jsfiddle.net/phongphan117/a212my20/
I have a json variable and use jquery.each()to write html tag and create loop for add class in the object. If you look at my code, it's not work correctly. How to fix them?
var db = {
"class" : [
{
"appearance": ["red-bg", "white-text"]
},
{
"appearance": ["yellow-bg", "black-text"]
},
{
"appearance": "red"
},
{
"appearance": "yellow"
},
{
"appearance": ""
}
]
}
$.each(db.class, function (key, data) {
console.log(data);
$('main').append('<div class="box">text</div>');
for (i=0; i<data.appearance.length; i++) {
var classtext = data.appearance[i].toString();
$('.box').addClass(classtext);
}
});
header, main, footer { padding-left: 0px; }
.box { width: 100px; height: 100px; }
.red-bg { background-color: red; }
.yellow-bg { background-color: yellow; }
.white-text { color: white; }
.black-text { color: black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.isotope/2.2.1/isotope.pkgd.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.96.1/css/materialize.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.96.1/js/materialize.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<main>
</main>
The problem is, you're passing some array and some strings, so when you have an array, the items are each item inside, ie:
["red-bg", "white-text"]
[0] = "red-bg"
[1] = "white-text"
but when it's a string, each item is a letter, ie:
"red"
[0] = "r"
[1] = "e"
[2] = "d"
so you can just update your class array to:
"class" : [
{
"appearance": ["red-bg", "white-text"]
},
{
"appearance": ["yellow-bg", "black-text"]
},
{
"appearance": ["red"]
},
{
"appearance": ["yellow"]
},
{
"appearance": [""]
}
]
you'll also have to update your each function, since you're adding the classes to the same .box.
$('.box:last-child').addClass(data.appearance[i]);
Now you're adding the data.appearance to your last .box inserted!
and it'll works! see the jsfiddle https://jsfiddle.net/2z95ye56/1/