WinJs List View display:none tile with ID - javascript

So what's the problem. I want to exclude item from WinJS List View with specific parameter (ID - passed from JSON). How to do that?
Things i've tried:
a) Before pushing data to someView.itemDataSource process it with this function (It work's, but looks dirty).
fldView.itemDataSource = this._processItemData(Data.items.dataSource);
....
_processItemData: function (data) {
for (var i = data.list.length; i >= 1; i--) {
if (data.list._groupedItems[i]) {
if (data.list._groupedItems[i].groupKey == 'Folders')
continue;
else {
if (data.list._groupedItems[i].data.folderID) {
data.list.splice(i - 1, 1);
}
}
}
}
return data;
}
b) The traditional way with two conditional templates (Doesn't work):
fldView.itemTemplate = this.getItemTemplate;
....
getItemTemplate: function(promise){
return promise.then(function(item){
var
itemTemplate = null,
parent = document.createElement("div");
if(item.data.folderID){
itemTemplate = document.querySelector('.hideItemTemplate')
}else{
itemTemplate = document.querySelector('.itemTemplate')
}
//console.log(item.data.folderID);
itemTemplate.winControl.render(item.data, parent);
return parent;
})
}
2 HTML templates
<div class="itemTemplate" data-win-control="WinJS.Binding.Template">
<div class="item">
<img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
<div class="item-overlay">
<h4 class="item-title" data-win-bind="textContent: title" style="margin-left: 0px;"></h4>
<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle" style="margin-left: 0px; margin-right: 4.67px;"></h6>
</div>
</div>
</div>
<div class="hideItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="display-none"></div>
</div>
and CSS display: none
.hideItemTemplate, .display-none{
display:none;
}
Thank's in advance!

Suggest to filter the item either before building the WinJS.Binding.List using array.filter or do a filter projection on the list after it is built. if grouping is required, grouping can be done on the filtered list.
var list; // assuming this is all data items
var filteredList = list.createFiltered(function filter(item)
{
if (item.FolderID)
return false;
else
return true;
});
var groups = filteredList.createGrouped(...);

Related

AngularJS ng-repeat is slow

It is not like it is slow on rendering many entries. The problem is that whenever the $scope.data got updated, it adds the new item first at the end of the element, then reduce it as it match the new $scope.data.
For example:
<div class="list" ng-repeat="entry in data">
<h3>{{entry.title}}</h3>
</div>
This script is updating the $scope.data:
$scope.load = function() {
$scope.data = getDataFromDB();
}
Lets say I have 5 entries inside $scope.data. The entries are:
[
{
id: 1,
title: 1
},
{
id: 2,
title: 2
},
......
]
When the $scope.data already has those entries then got reloaded ($scope.data = getDataFromDB(); being called), the DOM element for about 0.1s - 0.2s has 10 elements (duplicate elements), then after 0.1s - 0.2s it is reduced to 5.
So the problem is that there is delay about 0.1s - 0.2s when updating the ng-repeat DOM. This looks really bad when I implement live search. Whenever it updates from the database, the ng-repeat DOM element got added up every time for a brief millisecond.
How can I make the rendering instant?
EDITED
I will paste all my code here:
The controller:
$scope.search = function (table) {
$scope.currentPage = 1;
$scope.endOfPage = false;
$scope.viewModels = [];
$scope.loadViewModels($scope.orderBy, table);
}
$scope.loadViewModels = function (orderBy, table, cb) {
if (!$scope.endOfPage) {
let searchKey = $scope.page.searchString;
let skip = ($scope.currentPage - 1) * $scope.itemsPerPage;
let searchClause = '';
if (searchKey && searchKey.length > 0) {
let searchArr = [];
$($scope.vmKeys).each((i, key) => {
searchArr.push(key + ` LIKE '%` + searchKey + `%'`);
});
searchClause = `WHERE ` + searchArr.join(' OR ');
}
let sc = `SELECT * FROM ` + table + ` ` + searchClause + ` ` + orderBy +
` LIMIT ` + skip + `, ` + $scope.itemsPerPage;
sqlite.query(sc, rows => {
$scope.$apply(function () {
var data = [];
let loadedCount = 0;
if (rows != null) {
$scope.currentPage += 1;
loadedCount = rows.length;
if (rows.length < $scope.itemsPerPage)
$scope.endOfPage = true
for (var i = 0; i < rows.length; i++) {
let item = rows.item(i);
let returnObject = {};
$($scope.vmKeys).each((i, key) => {
returnObject[key] = item[key];
});
data.push(returnObject);
}
$scope.viewModels = $scope.viewModels.concat(data);
}
else
$scope.endOfPage = true;
if (cb)
cb(loadedCount);
})
});
}
}
The view:
<div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
<div class="row note-list" ng-if="showList">
<h3>Notes</h3>
<input ng-model="page.searchString" id="search"
ng-keyup="search('notes')" type="text" class="form-control"
placeholder="Search Notes" style="margin-bottom:10px">
<div class="col-12 note-list-item"
ng-repeat="data in viewModels track by data.id"
ng-click="edit(data.id)"
ontouchstart="touchStart()" ontouchend="touchEnd()"
ontouchmove="touchMove()">
<p ng-class="deleteMode ? 'note-list-title w-80' : 'note-list-title'"
ng-bind-html="data.title"></p>
<p ng-class="deleteMode ? 'note-list-date w-80' : 'note-list-date'">{{data.dateCreated | displayDate}}</p>
<div ng-if="deleteMode" class="note-list-delete ease-in" ng-click="delete($event, data.id)">
<span class="btn fa fa-trash"></span>
</div>
</div>
<div ng-if="!deleteMode" ng-click="new()" class="add-btn btn btn-primary ease-in">
<span class="fa fa-plus"></span>
</div>
</div>
<div ng-if="!showList" class="ease-in">
<div>
<div ng-click="back()" class="btn btn-primary"><span class="fa fa-arrow-left"></span></div>
<div ng-disabled="!isDataChanged" ng-click="save()" class="btn btn-primary" style="float:right">
<span class="fa fa-check"></span>
</div>
</div>
<div contenteditable="true" class="note-title"
ng-bind-html="selected.title" id="title">
</div>
<div contenteditable="true" class="note-container" ng-bind-html="selected.note" id="note"></div>
</div>
</div>
<script src="../js/pages/note.js"></script>
Calling it from:
$scope.loadViewModels($scope.orderBy, 'notes');
The sqlite query:
query: function (query, cb) {
db.transaction(function (tx) {
tx.executeSql(query, [], function (tx, res) {
return cb(res.rows, null);
});
}, function (error) {
return cb(null, error.message);
}, function () {
//console.log('query ok');
});
},
It is apache cordova framework, so it uses webview in Android emulator.
My Code Structure
<html ng-app="app" ng-controller="pageController">
<head>....</head>
<body>
....
<div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
....
</div>
</body>
</html>
So there is controller inside controller. The parent is pageController and the child is noteController. Is a structure like this slowing the ng-repeat directives?
Btw using track by is not helping. There is still delay when rendering it. Also I can modify the entries as well, so when an entry was updated, it should be updated in the list as well.
NOTE
After thorough investigation there is something weird. Usually ng-repeat item has hash key in it. In my case ng-repeat items do not have it. Is it the cause of the problem?
One approach to improve performance is to use the track by clause in the ng-repeat expression:
<div class="list" ng-repeat="entry in data track by entry.id">
<h3>{{entry.title}}</h3>
</div>
From the Docs:
Best Practice: If you are working with objects that have a unique identifier property, you should track by this identifier instead of the object instance, e.g. item in items track by item.id. Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones. For large collections, this significantly improves rendering performance.
For more information, see
AngularJS ngRepeat API Reference -- Tracking and Duplicates
In your html, try this:
<div class="list" ng-repeat="entry in data">
<h3 ng-bind="entry.title"></h3>
</div>
After thorough research, I found my problem. Every time I reset / reload my $scope.viewModels I always assign it to null / empty array first. This what causes the render delay.
Example:
$scope.search = function (table) {
$scope.currentPage = 1;
$scope.endOfPage = false;
$scope.viewModels = []; <------ THIS
$scope.loadViewModels($scope.orderBy, table);
}
So instead of assigning it to null / empty array, I just replace it with the new loaded data, and the flickering is gone.

How to check if 2 <img> tags contain the same image

I am making a Memory cards game(where cards are flipped on their back and you must open and find the pairs)
I have 12 divs with 12 images and there 6 pairs of images. How can I write JS or jQuery code to check if the images are the same ?
I added data index to divs with setAttribute but on console.log they print undefined.
<div class="frontCard">
<img src="frontCard1.jpg" alt="">
</div>
<div class="frontCard">
<img src="frontCard2.jpg" alt="">
</div>
<div class="frontCard">
<img src="frontCard1.jpg" alt="">
</div>
and js code
const item = document.querySelectorAll(".card");
item.forEach((item, index) => {
item.setAttribute("data-index", index);
})
var openCards = 0;
var points=0;
$(".card").click(function() {
if (openCards >= 2) {
$(".card").removeClass('cardOpen');
$(this).addClass('cardOpen');
openCards = 1;
} else {
$(this).addClass('cardOpen');
openCards++;
if (openCards == 2) {
if (true)//this is where i need the condition
{
const openCard = document.querySelectorAll(".cardOpen");
console.log("index:" + openCard.index);
points++;
console.log(points);
}
}
}
});
basically there are multiple things wrong in your js.
First of - shouldn't .card be .frontCard?
I got a code which is working as expected, even though it might not be the most beuatiful.
Check out the snippet
const item = document.querySelectorAll(".frontCard");
item.forEach((item, index) => {
item.setAttribute("data-index", index);
})
var openCards = 0;
var points=0;
$(".frontCard").click(function() {
if (openCards >= 2) {
$(".frontCard").removeClass('cardOpen');
openCards = 1;
$(this).addClass('cardOpen');
} else {
$(this).addClass('cardOpen');
openCards++;
if (openCards == 2) {
if ($($(".cardOpen")[0]).find("img")[0].src == $($(".cardOpen")[1]).find("img")[0].src){
const openCard = document.querySelectorAll(".cardOpen");
console.log("index:" + openCard.index);
points++;
console.log(points);
$(".frontCard").removeClass('cardOpen');
}else{
console.log("NoPoints")
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="frontCard">
<img src="https://seomofo-da30.kxcdn.com/wp-content/uploads/2010/05/google_logo_new.png" alt="test">
</div>
<div class="frontCard">
<img src="https://i.dlpng.com/static/png/9013_preview.png" alt="test2">
</div>
<div class="frontCard">
<img src="https://seomofo-da30.kxcdn.com/wp-content/uploads/2010/05/google_logo_new.png" alt="test3">
</div>
Assuming that your cards are using the same image, you could check against that. This would require adding ID fields to each card in order to compare the individual cards.
A better way to do this would be to write some JavaScript to contain the cards in an array and fill the array with some ints such as [0,0,1,1,2,2...]. You could then use that to shuffle the cards, or display them and check their values. Just leave yourself a comment as to which number corresponds to which card:)
This would also allow you to display them dynamically if you always start with the back image and then replace the element by ID with the appropriate new image (or lay it over top of it to hide it)

jQuery loop one to one

I have problem with my code.
I have products on my website, each product has his own <a><h1>CODE</h1></a> and I need to take this CODE and paste it before an image. I need to copy element with has class="loop1" and paste it into another element with class="lop1" and then take another element with class="loop2" and paste into element with class="lop2" and so on..
I made class with same numbers for easier copying, but it doesnt work. Can sombody help me?
This is my code:
$('#loop').addClass(function(i) {
return 'lop'+(i+1);
});
$('.p-name').addClass(function(i) {
return 'loop'+(i+1);
});
function doForm() {
var numb = ["1","2","3","4","5","6","7","8","9","10","11","13","14"];
for (var i=0;i<numb.length;i++) {
number = numb[i];
selector = '.loop' + number;
if ($(selector).length != 0) {
val = $(selector).html();
$('lop' + number).html(val);
}
}
}
doForm();
Related html:
<div class="columns">
<div id="loop" class="lop1"></div>
<div class="p-image">
<img src="https://" width="290" height="218">
</div>
<div class="p-info">
<span itemprop="name">PRODUCT</span>
</div>
<div>
So I need to take from "p-info > a" and paste it into div "lop1". Depends on number in class copy and paste HTML into div with same number.
Change $('lop' + radek).html(val); to $('.lop' + number).html(val);
Notice the . at the beginning of lop, it will create a selector to fetch element based on the class.
$('#loop').addClass(function(i) {
return 'lop'+(i+1);
});
$('.p-name').addClass(function(i) {
return 'loop'+(i+1);
});
function doForm() {
var numb = ["1","2","3","4","5","6","7","8","9","10","11","13","14"];
for (var i=0;i<numb.length;i++) {
var number = numb[i];
var selector = '.loop' + number;
if ($(selector).length != 0) {
var val = $(selector).html();
$('.lop' + number).html(val);
}
}
}
doForm();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="columns">
<div id="loop" class="lop1"></div>
<div class="p-image">
<img src="https://" width="290" height="218">
</div>
<div class="p-info">
<span itemprop="name">PRODUCT</span>
</div>
<div>

Javascript HTML .children traversal

I'm trying to go through all of the elements in the document and pull the ones with a target class name. Importantly, I'm needing to do it without the use of document.getElementsByClassName(className) / document.querySelectorAll, etc. — that's the point of this learning exercise.
Here's the javascript:
var getElementsByClassName = function(className){
var rootElem = document.body;
var collectionResult = [];
if (rootElem.getAttribute("class") == className) {
collectionResult.push(rootElem);
};
var nextTier = function(collectionResult, rootElem) {
var thisTier = rootElem.children;
for (i=0; i<thisTier.length; i++) {
var classes = thisTier[i].getAttribute("class");
if (classes != undefined && classes.includes(className)) {
collectionResult.push(thisTier[i]);
};
var childrenArray = thisTier[i].children;
if (childrenArray.length > 0) {
nextTier(collectionresult, childrenArray)
};
};
};
nextTier(collectionResult, rootElem);
return collectionResult;
};
Here's the section of the HTML structure I'm having trouble with:
<p>
<div class="somediv">
<div class="innerdiv">
<span class="targetClassName">yay</span>
</div>
</div>
</p>
The code works for the rest of the page with any number of non-nested elements. But as soon as var childrenArray = thisTier[i].children get to the div.somediv element, it has childrenArray == undefined rather than pulling the div.innerdiv element.
Am I misunderstanding how element.children works?
Array.prototype.flatMap is an effective tool for flattening trees (like DOM) to an array of values (like a list of elements) -
function getElementsByClassName (node, query) {
function matchAll (children) {
return Array
.from(children)
.flatMap(c => getElementsByClassName(c, query))
}
if (node.classList && node.classList.contains(query))
return [ node, ...matchAll(node.childNodes) ]
else
return matchAll(node.childNodes)
}
const result =
getElementsByClassName(document, "targetClassName")
console.log(result)
// [ <div class="somediv targetClassName">…</div>
// , <span class="targetClassName">yay1</span>
// , <span class="targetClassName">yay2</span>
// , <span class="targetClassName">yay3</span>
// ]
<div class="somediv targetClassName">
<div class="innerdiv">
<span class="targetClassName">yay1</span>
</div>
</div>
<div class="somediv">
<div class="innerdiv">
<span class="targetClassName">yay2</span>
</div>
</div>
<div class="somediv">
<div class="innerdiv">
<span class="targetClassName">yay3</span>
</div>
</div>
You seem to be overcomplicating things.
function getElementsByClassName(className, root) {
if(!root) root = document.documentElement;
return [].reduce.call(root.children, function(arr, child) {
if(child.classList.contains(className)) arr.push(child);
return arr.concat(getElementsByClassName(className, child))
}, []);
}
function getElementsByClassName(className, root) {
if(!root) root = document.documentElement;
return [].reduce.call(root.children, function(arr, child) {
if(child.classList.contains(className)) arr.push(child);
return arr.concat(getElementsByClassName(className, child))
}, []);
}
console.log(getElementsByClassName("targetClassName"));
<div class="somediv targetClassName">
<div class="innerdiv">
<span class="targetClassName">yay1</span>
</div>
</div>
<div class="somediv targetClassName">
<div class="innerdiv targetClassName">
<span class="targetClassName">yay2</span>
</div>
</div>
<div class="somediv">
<div class="innerdiv">
<span class="targetClassName">yay3</span>
</div>
</div>

Adjust ng-repeat to match object structure

I have an object which looks like this:
$scope.hobbies = {
list: [
{
"PersonId": 23,
"PersonName": "John Smith",
"Hobbies": [
{
"HobbyTitle": "Paragliding",
"HobbyId": 23
},
{
"HobbyTitle": "Sharking",
"HobbyId": 99
}
]
}
]
};
I'm trying to develop a view which allows users to make a selection of each person's hobby.
I have a plunker here
My problem is that all selected hobbies are displayed under every person. This is because I'm just pushing all selected hobbies to a selectedHobbies Array.
$scope.addHobbyItem = function (item) {
var index = $scope.selectedHobbies.list.indexOf(item);
if (index === -1) {
$scope.selectedHobbies.list.push(item);
}
};
This of course doesn't work, as once a hobby is selected, it is shown under every person. How could I adjust the code to work with the way I'm ng-repeating over the selectedHobbies?
The HTML is below. I'm also using a directive to listen to click on the hobby container and trigger addHobbyItem()
<div data-ng-repeat="personHobby in hobbies.list">
<div>
<div style="border: 1px solid black; margin: 10px 0 10px">
<strong>{{ personHobby.PersonName }}</strong>
</div>
</div>
<div class="">
<div class="row">
<div class="col-xs-6">
<div data-ng-repeat="hobby in personHobby.Hobbies" data-ng-if="!hobby.selected">
<div data-hobby-item="" data-selected-list="false" data-ng-class="{ selected : hobby.selected }"></div>
</div>
</div>
<div class="col-xs-6">
<div data-ng-repeat="hobby in selectedHobbies.list">
<div data-hobby-item="" data-selected-list="true"></div>
</div>
</div>
</div>
</div>
</div>
your selectedHobbies should be a map in which the key is the person id and the value is a list of his selected hobbies. checkout this plunker
$scope.selectedHobbies = {
map: {}
};
// Add a hobby to our selected items
$scope.addHobbyItem = function(pid, item) {
if(!$scope.selectedHobbies.map[pid]) {
$scope.selectedHobbies.map[pid] = [];
}
var index = $scope.selectedHobbies.map[pid].indexOf(item);
if (index === -1) {
$scope.selectedHobbies.map[pid].push(item);
}
};
in the directive call addHobbyItem with the person id
scope.addHobbyItem(scope.personHobby.PersonId, scope.hobby);
and lastly in you html iterate on each person's selected hobbies
<div data-ng-repeat="hobby in selectedHobbies.map[personHobby.PersonId]">
<div data-hobby-item="" class="add-remove-container--offence" data-selected-list="true"></div>
</div>
something like this:
http://plnkr.co/edit/7BtzfCQNTCb9yYkv1uPN?p=preview
I used the $parent.$index to create an array of arrays containing hobbies for each person.
$scope.addHobbyItem = function (item, index) {
var ine = $scope.selectedHobbies[index].indexOf(item);
if (ine === -1) {
$scope.selectedHobbies[index].push(item);
}
};
function hobbyClickEvent() {
if (!$(element).hasClass('selected')) {
scope.addHobbyItem(scope.hobby, scope.$parent.$index);
} else {
scope.removeHobbyItem(scope.hobby);
}
}
and in the HTML:
<div data-ng-repeat="hobby in selectedHobbies[$index]">
<div data-hobby-item="" class="add-remove-container--offence" data-selected-list="true"></div>
</div>

Categories