Scraping html page result.. not in the right order - javascript

I'm trying to get data from this page using cheerio js:
var html =
"<div class='clear'>" +
"<div class='time_head'>time_head content1</div>"
+ "<div class='blockfix'>blockfix1</div>"
+ "<div class='blockfix'>blockfix2</div>"
+ "<div class='time_head'>time_head content2</div>"
+ "<div class='blockfix'>blockfix3</div>"
+ "<div class='blockfix'>blockfix4</div>"
+ "<div class='blockfix'>blockfix5</div>"
+ "</div>";
this is what i tried so far:
$ = cheerio.load(html);
let devtoList = [];
$('.clear').each(function (i, elem) {
devtoList[i] = {
title: $(this).find('.time_head').text(),
game: $(this).find('.blockfix').text()
};
});
const devtoListTrimmed = devtoList.filter(n => n != undefined);
console.log(devtoListTrimmed);
the result is :
[
{ title: 'time_head content1time_head content2',
game: 'blockfix1blockfix2blockfix3blockfix4blockfix5' }
]
But i need every time_head with its blockfix's
TIME_HEAD CONTENT1
----blockfix1
----blockfix2
TIME_HEAD CONTENT2
----blockfix3
----blockfix4
please note:
1- The number of time_head's always change
2- I'm open to other solutions

Get all elements with .time_head, iterate over it, apply while loop until the next element has class blockfix.
const output = [];
$('.time_head').each(function(i) {
let next = $(this).next('.blockfix');
output.push({"title": $(this).text(), game: []});
while(next) {
output[i].game.push(next.text());
const isNext = $(next).next('.blockfix');
next = isNext.length > 0 ? $(next).next('.blockfix') : false;
}
});
console.log(output);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='clear'>
<div class='time_head'>time_head content1</div>
<div class='blockfix'>blockfix1</div>
<div class='blockfix'>blockfix2</div>
<div class='time_head'>time_head content2</div>
<div class='blockfix'>blockfix3</div>
<div class='blockfix'>blockfix4</div>
<div class='blockfix'>blockfix5</div>
</div>

Related

How to avoid this duplication of code? I wanted to repeat the same code 25 times

I didn't want to repeat the same code 25 times in the part of "getElementById", "carregaItem(iVid)" and " < div id="item"> < /div> ". The BDVYT array has 25 elements I just put 2 for simplicity. There must be some way to do it but I'm not getting there.
var BDVYT = [{durac:27},{durac:23},];
refItem = document.getElementById("item");
refItem2 = document.getElementById("item2");
function inic(){
mostraItem();
}
function mostraItem(){
carregaItem(0);
carregaItem1(1);
//-----------
//until carregaItem24(24)
}
function carregaItem(iVid){
var codHTML = "";
codHTML += '<div id="itemInfo">';
codHTML += 'duração: <b>' + duracVid(iVid) + 'minutos</b>';
codHTML += '</div>';
refItem.innerHTML = codHTML;
}
function carregaItem1(iVid){
var codHTML = "";
codHTML += '<div id="itemInfo2">';
codHTML += 'duração: <b>' + duracVid(iVid) + 'minutos</b>';
codHTML += '</div>';
refItem2.innerHTML = codHTML;
}
//until carregaItem24(iVid)
function duracVid(iVid){
return BDVYT[iVid].durac;
}
<body onload="inic()">
<div id="item"></div>
<div id="item2"></div>
</body>
Use classes for the refItems instead, and when iterating over the collection of them, use the index being iterated over to find the associated element in the BDVYT array.
var BDVYT = [{durac:27},{durac:23},];
const refItems = document.querySelectorAll('.item');
refItems.forEach((item, i) => {
item.innerHTML = `
<div id="itemInfo2">
duração: <b>${BDVYT[i].durac} minutos</b>
</div>
`;
});
<body>
<div class="item"></div>
<div class="item"></div>
</body>

how to get the text inside the dynamically rendered items on the click of the button?

I am displaying the content after fetching it via ajax using jquery so the content is dynamic,
var i, j;
var product_names = [];
var product_image = [];
var product_rate = [];
var product_amount = [];
var product_category = [];
$.ajax({
url: BASE_URL + '/getOrgServices?orgId=' + orgId,
type: 'GET',
success: function(result) {
//console.log(result);
for (i in result) {
product_names[i] = result[i].name;
product_image[i] = result[i].imgurl;
product_rate[i] = result[i].rate;
product_amount[i] = result[i].amount;
product_category[i] = result[i].categories;
}
for(i in product_category){
var html = '<div class="prod-content"><ul id="products">';
html += '<li class="row product-listing">';
html += '<div class="product-img-container"><img class="product-img" src="' + product_image[i] + '"></div>';
html += '<div class="container product-listing-details"><h6 id="product-title">' + product_names[i] + '</h6><div id="product-description">' + product_amount[i] + '</div><br><div id="product-cost">' + product_rate[i] + '</div></div>';
html += '<div id="product-add">';
html += '<button class="add-button" type="button" role="button">ADD</button>';
html += '<span class="quantity"><input class="item-quantity" type="number" value="0" min="0" max="50" step="1"></span>';
html += '</div></li> </ul></div>'
//adding elements to all category
var id_str = '#' + new_id[0];
$(id_str).append(html);
}
}
Now in the below function i want to access the name of one h6 product_name at a time so is there any method to do so like if I could select
$(".add-button").click(function() {
//here i want to add items to cart on button click (means the button clicked its corresponding item should be added to cart )
});
is the cart part of the html code
<div class="order">
<ul id="products">
<li>
//here i want to append the added item
</li>
</ul>
</div>
To get all h6 you can do similar thing as below, without jQuery.
$(".add-button").click(function() {
console.log("click");
document.querySelectorAll("div.prod-content h6").forEach((function(elem) {
console.log(elem.innerText);
if (parseInt(elem.innerText) > 0) {
console.log(elem);
}
}));
});

Live search and filter JavaScript

I have a page with just the search bar; I wish to pull in content from an API, filter them on input[search], then display matches on the page. More like what this plugin does: https://lscf.pixolette.com/photography/ How can I achieve this, please?
Currently, I have this code. What am i doing wrong, please?
const search = document.getElementById('search_box');
const searchResults = document.getElementById('search-results');
const searchMessage = document.getElementById('search-message');
let users;
// Get users
const getUsers = async () => {
const res = await fetch('baseUrl/wp-json/wp/v2/posts');
users = await res.json();
};
// FIlter states
const searchUsers = searchText => {
// Get matches to current text input
let matches = users.filter(user => {
const regex = new RegExp(`^${searchText}`, 'gi');
// return user.displayName.match(regex) ||
user.abbr.match(regex);
});
console.log(matches);
// Clear when input or matches are empty
if (searchText.length === 0) {
matches = [];
searchResults.innerHTML = '';
}
outputHtml(matches);
};
// Show results in HTML
const outputHtml = matches => {
if (matches.length > 0) {
const html = matches.map(match =>
`<div class="card card-body mb-1">
<h4>${match.title.rendered} (${match.id})
<span class="text-primary">${match.userPrincipalName}.
</span></h4>
<small>ID: ${match.id} / Language:
${match.preferredLanguage}</small>
</div>`
)
.join('');
searchResults.innerHTML = html;
}
};
window.addEventListener('DOMContentLoaded', getUsers);
search.addEventListener('input', () => searchUsers(search.value));
An example using the WordPress API
Search box
<div>
<h4>Search blog by title</h4>
<div class="form-group ">
<input type="text" name="search_box" id="search_box" class="form-control" placeholder="Search by slug e.g my-title" onfocus="this.value=''" >
</div>
</div>
DISPLAY RESULTS
<table id='results'>
</table>
SEARCH BOX AJAX
//setup before functions
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms (5 seconds)
//on keyup, start the countdown
$('#search_box').keyup(function(){
clearTimeout(typingTimer);
if ($('#search_box').val()) {
var text = $('#search_box').val();
typingTimer = setTimeout(doneTyping(text), doneTypingInterval)
}
});
//user is "finished typing," do something
function doneTyping (text) {
//do something
// var text = text;
var api_url_search = `/wordpress/wp-json/wp/v2/posts?slug=${text}`;
$.ajax({
url:api_url_search,
dataType: 'json',
success: function(response){
var len = response.length;
for(var i=0; i<len; i++){
var id = response[i].id;
var date = response[i].date_gmt;
var slug = response[i].slug;
var excerpt = response[i].excerpt.rendered;
var categories = response[i].categories;
var search_str =
'<td>'+
'<div class="card" style="width: 300px;">' +
'<div class="card-divider">' + (i+1) + ' ' + slug + '</div>' +
' <div class="card-section">' +
'<p>' + excerpt + '</p>' +
'<h4>' + date + '</h4>' +
'<h4>' + 'Category:' + categories + '</h4>' +
'<a href="somepage.php?">'+
'<input type="button" value="read more">' + '</input>' +
' </a>' +
' </div>' +
'</div>'+
'</td>'
;
$('#results').empty().append(search_str);
}
}
});
};

javascript logic to append/prepend div on html template

I wrote a very basic web app that pulls recipe data back from an API. The data is rendered via being pushed to an html template defined in the javascript file. The layout is controlled via a float-grid in CSS.
The code portion that renders the result and pushes to the template:
function displayRecipeSearchData(data) {
var results = ' ';
if (data.hits.length) {
data.hits.forEach(function(item) {
results += template.item(item);
});
}
else {
results += '<p> No results </p>';
}
$('#js-search-results').html(results);
}
The html template through which responses are displayed:
const template = {
item: function(item) {
return '<div class ="col-4">' +
'<div class ="result">' +
'<div class="recipelabel">' +
'<div class="reclist">' + item.recipe.ingredientLines + '</div><!-- end reclist -->' +
'<p class="label">' + item.recipe.label + '</p>' +
'<div class="thumbnail">' +
'<a href="'+ httpsTransform(item.recipe.url) + '" target="_blank">' +
'<img src="' + item.recipe.image + '"alt="' + item.recipe.label + '">' +
'</a>' +
'<div class="recipesource">' +
'<p class="source">' + item.recipe.source + '</p>' +
'</div><!-- end recipesource -->' +
'</div><!-- end thumbnail -->' +
'</div><!-- end recipelabel -->' +
'</div><!-- end result -->' +
'</div><!-- end col-4 -->';
}
};
I am trying to change the logic in the displayRecipeSearchData function such that, for each group of three results, a <div></div> surrounds the block of three results. This is so the rows/columns always work in the flex grid. I have tried several ways but have yet to get the syntax/logic correct. Would an if statement nested in the existing statement be effective?
if(i % 3 === 0 ){ results. += '<div class="row">''</div>'}
Any suggestions would be appreciated.
You could use another variable for storing one row of HTML:
function displayRecipeSearchData(data) {
var results = ' ', row = '';
if (data.hits.length) {
data.hits.forEach(function(item, i) {
row += template.item(item);
if (i % 3 == 2) { // wrap row and add to result
results += '<div class="row">' + row + '</div>';
row = '';
}
});
if (row.length) { // flush remainder into a row
results += '<div class="row">' + row + '</div>';
}
}
else {
results += '<p> No results </p>';
}
$('#js-search-results').html(results);
}
you are definitely doing this the hard way in my opinion.
instead of manually writing the template as a string and trying to inject the string at the right place (potentially creating invalid html) you should use javascripts built-in element creation. also it'll be more modular to create children in their own functions. It will also be much easier to use a function instead of an object to hold your object creator. My version may have a lot more code, but it will be much easier to modify in the long run
const Itemizer = function(){
this.items = [];
const createEl = function(elType, classes, attributes, text, html){
let el = document.createElement(elType)
for(let i = 0; i < classes.length; i++){
el.classList.add(classes[i]
}
for(let attr in attributes){
el.setAttribute(attr, attributes[attr])
}
if(text){
el.innerText = text
}
if(html){
el.innerHTML = html
}
return el
};
const createThumbnail = function(url, image, alt, source){
let thumbnail = createEl("DIV", ["thumbnail"]),
link = createEl("A", [], {href: httpsTransform(url)}),
img = createEl("IMG", [], {src: image, alt: label});
rSource = createRecipeSource(source)
link.appendChild(img);
thumbnail.appendChild(link);
thumbnail.appendChild(rSource)
return thumbnail
};
const createRecipeSource = function(source){
let wrapper = createEl("DIV", ["recipe-source"]);
wrapper.appendChild(createEl("P", ["source"], {}, source))
return wrapper
}
const createRecipeLabel = function({
recipe: {
ingredientLines,
label,
url,
source
}
}){
let labelWrapper = createEl("DIV", ["recipe-label"),
ingredients = createEl("DIV", ["rec-list"], {}, false, ingredientLines),
recipeLabel = createEl("P", ["label"], {}, label),
thumbnail = createThumbnail(url, image, label, source)
labelWrapper.appendChild(ingredients)
labelWrapper.appendChild(recipeLabel)
labelWrapper.appendChild(thumbnail)
return labelWrapper
}
const createNewItem = function(data){
let columnWrapper = createEl("DIV", ["col-4"]),
result = createEl("DIV", ["result"]),
label = createRecipeLabel(data)
columnWrapper.appendChild(result)
result.appendChild(label)
this.items.push(columnWrapper)
return columnWrapper
}.bind(this)
const getItems = function(){
return this.items
}.bind(this)
const getRows = function(){
const rows = []
let row;
for(let i = 0; i < this.items.length; i++){
const item = this.items[i]
if(i % 3 === 0){
row = createEl("DIV", ["row"])
rows.push(row)
}
row.appendChild(item)
}
return rows;
}.bind(this)
return {
add: createNewItem,
get: getItems,
rows: getRows
}
}
You can then use the function like so:
const template = new Itemizer()
function displayRecipeSearchData(data) {
let rows
if (data.hits.length) {
for(let i = 0; i < data.hits.length; i++){
template.add(data.hits[i])
}
rows = template.rows()
} else {
const p = document.createElement("P")
p.innerText = "No Results")
rows = [p]
}
const resultsWrapper = document.getElementById("js-search-results");
for(let i = 0; i < rows.length; i++){
resultsWrapper.appendChild(rows[i])
}
}
it's also good form to separate css classes with hyphens, so I replaced a few of your class names to reflect that
It's also important to note that you don't actually need more than 1 row. if you wrap all of your items in one row section columns will automatically overflow to the next row when they hit the grid limit
My last note is never use target blank. it goes against proper UX, and creates security holes in your application. if your users need to open in a new tab they can hold ctrl or click "open in new tab"

saving table data in json file and again get when click a button

I have a table which is editable. I want to save my table content after editing on click of a button. Then again when I click another button I want get this data back using JSON.
$('.logodiv2').on('click', function() {
var myjson = '{';
var mainobject = [];
var tbl2 = $('tr').each(function(tittle) {
x = $(this).children();
var subobject = [];
x.each(function() {
subobject.push('"' + $(this).text() + '"');
});
mainobject.push('"' + tittle + '": [' + subobject.join(',') + ']');
})
myjson += mainobject.join(",") + '}'
alert(JSON.stringify(myjson));
return myjson;
});
<div class="logodiv1">
<img src="images/folder.svg" />
</div>
<div class="logodiv2">
<img src="images/saveicon.svg" />
</div>

Categories