const data = '{{ search_user }}'
const rdata = JSON.parse(data.replace(/"/g, '"'))
const input = document.getElementById('user-input_products')
let filteredArr = []
input.addEventListener('keyup', (e)=>{
box.innerHTML = ""
filterMax = (fn, c) => x => c && fn(x) && c--
filter = profiles=> profiles['full_name'].includes(e.target.value)
max = 30
filteredArr = rdata.filter(filterMax(filter, max))
if (filteredArr.length > 0) {
filteredArr.map(profiles=>{
box.innerHTML += `<li>${profiles['full_name']}</li>`
})
} else {
box.innerHTML = "No result found"
}
})
And when i want to pass id to django url inside of innerHTML it's give me this error:
Could not parse the remainder: '${profiles["user"]}' from '${profiles["user"]}'
How can i fix that?
rdata:
[
{
"user": 28,
"full_name": "John"
},
{
"user": 35,
"full_name": "Robert"
},
{
"user": 37,
"full_name": "Mary"
},
{
"user": 38,
"full_name": "Jennifer"
},
]
You cannot mix server side Python code with frontend javascript code like this. Django Templates sees ${profiles['user']} as a simple string. To fix this you can store a path prefix in a JS variable and combine the user links with JS template literal:
const path_prefix = "{% url 'user_num' %}"
const data = '{{ search_user }}'
const rdata = JSON.parse(data.replace(/"/g, '"'))
const input = document.getElementById('user-input_products')
let filteredArr = []
input.addEventListener('keyup', (e)=>{
box.innerHTML = ""
filterMax = (fn, c) => x => c && fn(x) && c--
filter = profiles=> profiles['full_name'].includes(e.target.value)
max = 30
filteredArr = rdata.filter(filterMax(filter, max))
if (filteredArr.length > 0) {
filteredArr.map(profiles=>{
box.innerHTML += `<li>${profiles['full_name']}</li>`
})
} else {
box.innerHTML = "No result found"
}
})
If the path for the user_num view function is user_num/, then ${path_prefix}${profiles['user']} will produce e.g. user_num/28. You may have to create a new URL definition if user_num function requires an argument, or you can just put in the actual path directly, e.g.: user_num/${profiles['user']}.
Related
This Google Apps Script code Search YouTube results by keywords. I want to add View Count and Subscribes Count too.
Output Data
function youTubeSearchResults() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getValues();
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId,`https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
sheet.getRange(2, 2, modifyResults.length, modifyResults[0].length).setValues(modifyResults);
}
When your showing script is modified, how about the following modification?
Modified script:
function youTubeSearchResults() {
// 1. Retrieve values from column "A".
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getDisplayValues().filter(([a]) => a);
// 2. Retrieve your current values.
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId, `https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
// 3. Retrieve viewCounts and subscriberCounts.
const { videoIds, channelIds } = modifyResults.reduce((o, r) => {
o.videoIds.push(r[1]);
o.channelIds.push(r[6]);
return o;
}, { videoIds: [], channelIds: [] });
const limit = 50;
const { viewCounts, subscriberCounts } = [...Array(Math.ceil(videoIds.length / limit))].reduce((obj, _) => {
const vIds = videoIds.splice(0, limit);
const cIds = channelIds.splice(0, limit);
const res1 = YouTube.Videos.list(["statistics"], { id: vIds, maxResults: limit }).items.map(({ statistics: { viewCount } }) => viewCount);
const obj2 = YouTube.Channels.list(["statistics"], { id: cIds, maxResults: limit }).items.reduce((o, { id, statistics: { subscriberCount } }) => (o[id] = subscriberCount, o), {});
const res2 = cIds.map(e => obj2[e] || null);
obj.viewCounts = [...obj.viewCounts, ...res1];
obj.subscriberCounts = [...obj.subscriberCounts, ...res2];
return obj;
}, { viewCounts: [], subscriberCounts: [] });
const ar = [viewCounts, subscriberCounts];
const rr = ar[0].map((_, c) => ar.map(r => r[c]));
// 4. Merge data.
const res = modifyResults.map((r, i) => [...r, ...rr[i]]);
// 5. Put values on Spreadsheet.
sheet.getRange(2, 2, res.length, res[0].length).setValues(res);
}
When this script is run, the following flow is run.
Retrieve values from column "A".
Retrieve your current values.
Retrieve "viewCounts" and "subscriberCounts".
Merge data.
Put values on Spreadsheet.
References:
Videos: list
Channels: list
reduce()
I have
const url = "/aaa/222/ccc"
const label = "/aaa/bbb/ccc"
I need to show this in breadcrumbs with labels, but when click on /bbb call 222
I try to handle this
const url = "/aaa/2222/ccc"
const label = "/aaa/bbb/ccc"
breadcrumbs: any;
this.breadcrumbs = url.split('/')
.reduce((acc, cur, i) => {
const url1 = i === 0
? `${acc[i - 1].url1}/${label.split('/')[i]}`
: undefined;
const label1 = i === 0
? `${acc[i - 1].label1}/${url.split('/')[i]}`
: undefined;
const breadcrumb = {
url1,
label1
};
acc.push(breadcrumb);
return acc;
}, []);
var numbers = [175, 50, 25];
But this is output, and this is not correct
/aaa/bbb/ccc/aaa/bbb/ccc/aaa/sites/bbb/ccc/aaa/222
I need in UI aaa/bbb/ccc but in background aaa/222/ccc
Thnx
It looks like you want to build up an array of objects containing a label and url section.
The following would do that for you.
const url = "/aaa/222/ccc"
const label = "/aaa/bbb/ccc"
createObject(url,label) {
const urlArray = url.split("/").slice(1);
const labelArray = label.split("/").slice(1);
return urlArray.map((item, index) => {
return {url: item, label: labelArray[index]}
})
}
Look up the previous accumulated url/label if i > 0, not when i === 0 in your OP.
const url = "/aaa/2222/ccc";
const label = "/aaa/bbb/ccc";
const urlPaths = url.split("/");
const labelPaths = label.split("/");
const snackbar = urlPaths.reduce((acc, urlPath, i) => {
let path;
if (i === 0) {
path = {
url: "",
label: "",
};
} else {
const { url, label } = acc[i-1];
path = {
url: `${url}/${urlPath}`,
label: `${label}/${labelPaths[i]}`
};
}
acc.push(path);
return acc;
}, []).splice(1);
console.log(snackbar);
Outputs
[{
"url": "/aaa",
"label": "/aaa"
}, {
"url": "/aaa/2222",
"label": "/aaa/bbb"
}, {
"url": "/aaa/2222/ccc",
"label": "/aaa/bbb/ccc"
}]
EDIT: the .splice(1) trailing call removes the first snackbar path (which has empty strings for the URL/label).
https://github.com/smelukov/loftschool-example
i am creating my project in this envorement .
I created friends.json file in the root folder .
friends.json
{
"name": "Иван",
"lastName": "Петров",
"value": "5.24"
},
{
"name": "Иван",
"lastName": "Петров",
"value": "6.00"
},
{
"name": "Иван",
"lastName": "Петров",
"value": "4.54"
}
]
index.hbs
<div id="prev-results"></div>
<button id="loadButton">Load Results</button>
index.js
const loadButton = document.querySelector("#loadButton");
const result = document.querySelector('#prev-results');
loadButton.addEventListener('click', () => {
fetch('friends.json')
.then(response => {
if (response.status >= 400){
return Promise.reject();
}
return response.json();
})
.then(friends => {
result.innerHTML = '';
for (let friend of friends) {
const friendNode = createFriendNode(friend);
result.appendChild(friendNode);
}
})
.catch(() => console.error('Что-то пошло не так'));
});
function createFriendNode(friend) {
const div = document.createElement('div');
div.classList.add('friend');
div.textContent = `${friend.name} ${friend.lastName}`;
const result = document.createElement("a");
result.textContent = `${friend.value}`;
result.classList.add("result");
const label = document.createElement("a");
label.classList.add("result-label")
label.textContent = "mL/min/1.73m²";
div.appendChild(result);
div.appendChild(label);
return div;
}
Now i can get objects from friends.json and add them to the DOM , but how do i change friends.json with javascript ?
The client can't write back to the static file it's being served. This would be the use case for a database. For a JSON-like document object store that can be manipulated, you can use something like MongoDB.
I have a JSON file from an external source containing a bunch of conditions I'd like to test. Either in realtime, or by somehow converting everything.
Let's say I have an instance of my class Person, containing {age: 13, country: "Norway"}, and that I have an external JSON file containing the following "helpers":
{
"is_child": "age < 16",
"is_adult": "age >= 16 and age < 67",
"is_senior": "age > 67",
"is_scandinavian": "country == 'Norway' or country == 'Sweden' or country == 'Denmark'",
}
and another file containing, for example, tickets I'd like to present, for example, "NorwegianTickets.json"
{
"childTicket": "is_child and is_scandinavian",
"flexTicket": "is_scandinavian and (is_adult or is_senior)"
}
How can I apply this logic to my code? If I want to run the condition "flexTicket" on my "Person", how should I map all the logic? How do I translate the "stringed" conditions, such as "and"/"or", and "()"?
You can easily achieve this using the eval function that execute a string as javascript.
So the logic will be:
Get the different conditions as a javascript string (is_child, is_adult, ...)
This function replace all variables (written as a string) by there value.
For that you will need to create a dictionary to list them all with the corresponding value:
const varsToReplace = {
country: 'Norway',
age: 12
}
Then you replace this variable in a given condition using the replace method. The only trick here is that you need to search for country and not country (if you not add the extra space before and after, a variable like user_country could be replaced by user_Norway). Also keep in mind that if you replace by a string you should wrapp the value in '':
const getConditionString = (condition) => {
let replacedConditon = ` ${conditions[condition]} `
Object.keys(varsToReplace).forEach((variable) => {
const re = new RegExp(` ${variable} `, 'g');
let replaceValue = ` ${varsToReplace[variable]} `
// If the value is a string we should add ''
if (typeof varsToReplace[variable] === 'string') {
replaceValue = ` '${varsToReplace[variable]}' `
}
replacedConditon = replacedConditon.replace(re, replaceValue)
})
return replacedConditon
}
Get the test as a javascript string (is_child and is_scandinavian, ...)
This function getTestString will replace all conditions key by the javascript string using the previous function:
const getTestString = (test) => {
let replacedTest = ` ${tests[test]} `
Object.keys(conditions).forEach((condition) => {
const re = new RegExp(` ${condition} `, 'g');
replacedTest = replacedTest.replace(re, ` ( ${getConditionString(condition)} ) `)
})
return replacedTest
}
Replace the different operators to be 'js valid':
const replaceOperators = (string) => {
const operators = {
or: '||',
and: '&&'
}
Object.keys(operators).forEach((operator) => {
const re = new RegExp(` ${operator} `, 'g');
string = string.replace(re, ` ${operators[operator]} `)
})
return string
}
Execute the js string using eval:
const evalTest = (test) => {
let testAsString = replaceOperators(getTestString(test))
return eval(testAsString)
}
Here is the full example:
const country = 'Norway'
const age = 12
const varsToReplace = {
country,
age
}
const conditions = {
"is_child": "age < 16",
"is_adult": "age >= 16 and age < 67",
"is_senior": "age > 67",
"is_scandinavian": "country == 'Norway' or country == 'Sweden' or country == 'Denmark'"
}
const tests = {
"childTicket": "is_child and is_scandinavian",
"flexTicket": "is_scandinavian and ( is_adult or is_senior )"
}
const getConditionString = (condition) => {
let replacedConditon = ` ${conditions[condition]} `
Object.keys(varsToReplace).forEach((variable) => {
const re = new RegExp(` ${variable} `, 'g');
let replaceValue = ` ${varsToReplace[variable]} `
// If the value is a string we should add ''
if (typeof varsToReplace[variable] === 'string') {
replaceValue = ` '${varsToReplace[variable]}' `
}
replacedConditon = replacedConditon.replace(re, replaceValue)
})
return replacedConditon
}
const getTestString = (test) => {
let replacedTest = ` ${tests[test]} `
Object.keys(conditions).forEach((condition) => {
const re = new RegExp(` ${condition} `, 'g');
replacedTest = replacedTest.replace(re, ` ( ${getConditionString(condition)} ) `)
})
return replacedTest
}
const replaceOperators = (string) => {
const operators = {
or: '||',
and: '&&'
}
Object.keys(operators).forEach((operator) => {
const re = new RegExp(` ${operator} `, 'g');
string = string.replace(re, ` ${operators[operator]} `)
})
return string
}
const evalTest = (test) => {
let testAsString = replaceOperators(getTestString(test))
console.log(testAsString)
return eval(testAsString)
}
console.log(evalTest('childTicket'))
console.log(evalTest('flexTicket'))
I would go for creating a DSL for that purpose. It's fun. I've written one to just give you some idea about it. Beware, its not fully tested, lacks basic functionality such as array access. I believe you may find better examples in the internet.
class Node_ {
children: Node_[];
constructor() {
this.children = [];
}
addChild = (node: Node_) =>
this.children.push(node);
evaluate = (context: any): boolean | number | string => {
throw new Error('Missing implementation');
}
}
enum ExprType {
Eq = 'eq',
Gt = 'gt',
Lt = 'lt',
Gte = 'gte',
Lte = 'lte',
Get = 'get',
}
class ExprNode extends Node_ {
expr: string;
constructor(expr: string) {
super();
this.throwIfInvalidExpr(expr);
this.expr = expr.toLowerCase();
}
throwIfInvalidExpr(expr: string) {
switch (expr.toLowerCase()) {
case ExprType.Eq:
case ExprType.Gt:
case ExprType.Lt:
case ExprType.Gte:
case ExprType.Lte:
case ExprType.Get:
break;
default:
throw new Error(`Unexpected expression: ${this.expr}`);
}
}
evaluate = (context: any) => {
switch (this.expr) {
case ExprType.Get:
return this.evaluateAccess(context);
default:
return this.evaluateCmp(context);
}
}
evaluateAccess = (context: any) => {
this.throwIfInvalidAccessOperands();
const prop = this.children[0].evaluate(context) as string;
const newContext = context[prop];
const child = this.children[1];
if (child) {
return child.evaluate(newContext);
} else {
return newContext;
}
}
evaluateCmp = (context: any) => {
this.throwIfInvalidOperands();
const left = this.children[0].evaluate(context);
const right = this.children[1].evaluate(context);
switch(this.expr) {
case ExprType.Eq:
return left === right;
case ExprType.Gt:
return left > right;
case ExprType.Gte:
return left >= right;
case ExprType.Lt:
return left < right;
case ExprType.Lte:
return left <= right;
}
}
throwIfInvalidOperands = () => {
if (this.children.length !== 2) {
throw new Error(`Invalid operand count ${this.children.length}`);
}
}
throwIfInvalidAccessOperands = () => {
if (this.children.length === 0 ||
this.children.length > 2) {
throw new Error(`Invalid access operand count ${this.children.length}`);
}
}
}
class ValueNode extends Node_ {
value: string | number;
constructor(value: string, str?: boolean) {
super();
if (str) {
this.value = value as string;
} else {
const num = parseInt(value);
if (Number.isNaN(num)) {
throw new Error(`Invalid number: ${value}`);
}
this.value = num;
}
}
evaluate = (_: any) => {
return this.value;
}
}
function tokenize(value: string): Node_ {
let index = 0;
const nodeStack = [];
let token = '';
while (index < value.length) {
switch(value[index]) {
case '(':
{
const node = new ExprNode(token);
nodeStack.push(node);
token = '';
}
break;
case ')':
{
if (token) {
const node = new ValueNode(token);
nodeStack.push(node);
addToParent(nodeStack);
token = '';
}
addToParent(nodeStack);
}
break;
case "'":
case '"':
const str = consumeString(value, index);
index += str.length + 1;
token += str;
{
const node = new ValueNode(token, true);
nodeStack.push(node);
addToParent(nodeStack);
}
token = '';
break;
case ',':
if (token) {
const node = new ValueNode(token);
nodeStack.push(node);
addToParent(nodeStack);
token = '';
}
break;
case ' ':
break
default:
token += value[index];
}
index++;
}
return nodeStack[0];
}
function consumeString(value: string, index: number) {
const delimiter = value[index++];
let ret = '';
while (value[index] !== delimiter) {
ret += value[index];
index++;
}
return ret;
}
function addToParent(nodeStack: Node_[]) {
console.assert(nodeStack.length > 0);
const last = nodeStack.pop();
if (nodeStack.length > 0) {
const parent = nodeStack.pop();
parent.addChild(last);
nodeStack.push(parent);
} else {
nodeStack.push(last);
}
}
{
const ast = tokenize('EQ("origami", GET("name"))');
const context = { name: 'origami' };
const context2 = { };
console.assert(ast.evaluate(context) === true);
console.assert(ast.evaluate(context2) === false);
}
{
const ast = tokenize('EQ(5, 5)');
console.assert(ast.evaluate({}) === true);
const ast1 = tokenize('EQ("foo", "foo")');
console.assert(ast1.evaluate({}) === true);
const ast2 = tokenize('EQ("foo", "bar")');
console.assert(ast2.evaluate({}) === false);
const ast3 = tokenize('GTE(15, 10)');
console.assert(ast3.evaluate({}) === true);
}
{
const ast = tokenize('GET("info", GET("person", GET("age")))');
const context = { info: { person: { age: 21 } } };
console.assert(ast.evaluate(context) === 21);
}
{
const ast = tokenize('LTE(21, GET("info", GET("person", GET("age"))))');
const context = { info: { person: { age: 21 } } };
console.assert(ast.evaluate(context) === true);
const context2 = { info: { person: { age: 15 } } };
console.assert(ast.evaluate(context2) === false);
}
{
const ast = tokenize('EQ(GET("info", GET("person", GET("planet"))), "earth")');
const context = { info: { person: { planet: "mars" } } };
console.assert(ast.evaluate(context) === false);
}
{
const ast = tokenize('GT(GET("person1", GET("age")), GET("person2", GET("age")))');
const context = { person1: { age: 56 }, person2: { age: 21 } };
console.assert(ast.evaluate(context) === true);
const context2 = { person1: { age: 25 }, person2: { age: 44 } };
console.assert(ast.evaluate(context2) === false);
}
I omitted AND & OR expressions, but it should be clear how to add them.
In this scenario, the client should submit the data along with the constraints. For example:
{
"context": {
"person": {
"age": 44,
"planet": "saturn"
}
},
"constraints": {
"shouldFrom": "EQ('mars', GET('person', GET('planet')))",
"minimumAge": "GTE(40, GET('person', GET('planet')))"
}
}
And the receiver part takes the each constraints, tokenize them and evaluate them with given context.
I have two methods - sort and more_than, Here is my JS:
const originalData = [{
"id": 2,
"title": "ASD",
"number": 50,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
},
{
"id": 1,
"title": "FGH",
"number": 150,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
}
]
const data = [...originalData]
const info_container = document.querySelector('.info-container')
const sort = () => {
const newData = data.sort((a, b) => parseFloat(a.id) - parseFloat(b.id))
fetchData(newData)
}
const more_than = (e) => {
if (e) {
const newData = data.filter((a) => {
return parseFloat(a.number) > parseFloat(e)
})
fetchData(newData)
} else return
}
const clear_filters = () => {
const radio = document.querySelector('input[name="sort"]')
radio.checked = false
fetchData(originalData)
}
const fetchData = (data) => {
info_container.innerHTML = "";
data.forEach((item, index) => {
const img = document.createElement("img");
const title = document.createElement('h3')
const node = document.createTextNode(item.src);
const node_title = document.createTextNode(item.title)
title.appendChild(node_title)
img.src = item.src
info_container.appendChild(title)
info_container.appendChild(img);
})
}
window.onload = function() {
fetchData(originalData)
}
<div><input type="radio" name="sort" onclick="sort()" />sort</div>
<div>More <input min="1" max="1000" oninput="more_than(value)" type="number" name="pages" /> than</div>
<div><button onclick="clear_filters()">Clear</button></div>
<div class="info-container">
</div>
plunker: http://plnkr.co/edit/HFuL37jL9ZHbvI2lib6t?p=preview
I want to use them in the same time. Now my sort function disappears after more_than and vice versa. How to fix that?
Thanks for answers in advance!
You want to have a kind of "meta" instance which allows you to keep the application state intact. That means, you need a way to keep the user selection stored inside variables. In the example below, Store acts as a repository for your data which also knows how many "pages" should be displayed as well as if the data should be sorted by .id. Calling .getItems() returns you a list of sorted/unsorted values, optionally filtered by "pages". Please note that Store doesn't alter the original data. It instead returns a new copy of the original data every time you call .getItems().
const originalData = [{
"id": 2,
"title": "ASD",
"number": 50,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
},
{
"id": 1,
"title": "FGH",
"number": 150,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
}
]
const Store = data => {
let __sorted = false;
let __pages = 0;
const _Store = {
sorted() {
__sorted = true;
return _Store;
},
unsorted() {
__sorted = false;
return _Store;
},
setPages(num) {
if (typeof num === 'number' && !isNaN(num)) {
__pages = Math.abs(num); // ensure we have a positive number
}
return _Store;
},
unsetPages() {
__pages = 0; // revert back to default
return _Store;
},
getItems() {
let items = [...data];
if (__sorted) {
return __pages <= 0 ? items.sort((a, b) => Number(a.id) - Number(b.id)) :
/* else */ items.filter(a => Number(a.number) >= __pages).
sort((a, b) => Number(a.id) - Number(b.id))
}
return __pages <= 0 ? items :
/* else */ items.filter(a => Number(a.number) >= __pages);
}
};
return _Store;
};
const dataStore = Store(originalData);
const $checkSort = document.querySelector('input[name="sort"]');
const $inputPages = document.querySelector('input[name="pages"]');
const $resetFilters = document.querySelector('#filter-reset');
const $output = document.querySelector('.info-container')
function onCheckSorted (event) {
if (event.target.checked) {
dataStore.sorted();
} else {
dataStore.unsorted();
}
show();
}
function onChangePages (event) {
let v = Number(event.target.value.trim());
if (v && !isNaN(v)) {
dataStore.setPages(v);
} else {
dataStore.unsetPages();
}
show();
}
function onClickReset () {
$checkSort.checked = null; // update UI
$inputPages.value = null; // update UI
dataStore.unsetPages().unsorted();
show();
}
function show () {
$output.innerHTML = "";
dataStore.getItems().forEach((item, index) => {
const img = document.createElement("img");
const title = document.createElement('h3')
const node = document.createTextNode(item.src);
const node_title = document.createTextNode(item.title)
title.appendChild(node_title)
img.src = item.src
$output.appendChild(title)
$output.appendChild(img);
});
}
$checkSort.addEventListener('change', onCheckSorted);
$inputPages.addEventListener('input', onChangePages);
$resetFilters.addEventListener('click', onClickReset);
// kick off
show();
<div><input type="checkbox" name="sort" />sort</div>
<div>More <input min="1" max="1000" type="number" name="pages" /> than</div>
<div><button id="filter-reset">Clear</button></div>
<div class="info-container">
</div>