Click and open a new page based on the clicked element data - javascript

I am beginner in web development and I am practicing my fundamental html, css and javascript skills.
I have here a code snippet of a blog-like site that I am doing as practice. I am fetching the data on https://jsonplaceholder.typicode.com/posts and https://jsonplaceholder.typicode.com/users to put in the site. In the code below, you can see how I fetch the data and how do I put them. I also put a photo of the site itself.
I just want to ask a few questions.
You can see in the code that the title and the content is in anchor tag. I want to ask if, just using vanilla javascript, there is a way I can click the title or content of the post and it will open to a new tab and it will only show that post? I think I know how to do this in Vue.js using router-link and props, but I am practicing and I am wondering if this is doable using only plain Javscript.
If you look in the article tag in the HTML, it is quite long. So instead of doing a lot of document.createElement and .appendChild, what I did was I wrote the entire article tag in the HTML. Then in the setPost function, I just selected and cloned it using .cloneNode(true) and from there, I just changed the contents based on the post in the for loop. I want to ask if this is a good way to do this or should I just manually create and append elements? How would you do this?
Thanks a lot in advance!
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
// console.log(res);
if(res.ok) {
return res.json();
}
return Promise.reject(res);
})
.then(data => {
// console.log('data: ', data.slice(0, 3));
const posts = data.slice(0, 3);
setPost(posts);
// getNavLink();
})
.catch(err => console.log('error: ', err))
function setPost(posts) {
const main = document.querySelector('main');
const article = document.querySelector('article');
const loading = document.querySelector('#loading');
for(post of posts) {
const clone = article.cloneNode(true);
const title = clone.querySelector('.article-title');
const author = clone.querySelector('.author');
const body = clone.querySelector('.article-content');
const date = clone.querySelector('.date');
title.href = `#${post.title}`;
// title.target = '_blank';
title.textContent = post.title;
body.textContent = post.body + post.body + post.body + post.body;
date.textContent = new Date().toLocaleString();
(() => fetch(`https://jsonplaceholder.typicode.com/users/${post.userId}`)
.then(res => res.json())
.then(data => {
author.textContent = data.name + ',';
clone.classList.remove('display');
loading.remove();
})
.catch(err => console.log(err)))();
main.appendChild(clone);
}
article.remove();
}
<div class='container container-flex'>
<main class='main-content'>
<div id='loading'></div>
<article class='display'>
<div class='article-head'>
<a href='#walapa' class='article-title'></a>
<div class='article-details art-flex'>
<span class='author subtitle'></span>
<span class='spacer subtitle'></span>
<span class='date subtitle'></span>
</div>
</div>
<div class='article-body'>
<a href='#' class='article-content'></a>
</div>
<div class='article-spacer'></div>
</article>
</main>
<aside class='sidebar'>
Tags
</aside>
</div>

You can make use of the click event on the title then inside your event function you make use of the window.open() method.
https://www.w3schools.com/jsref/met_win_open.asp
let hello=document.getElementById("hello");
hello.addEventListener("click",()=>{
window.open(url of where you are fetching the dat)
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 id="hello">Hello</h1>
</body>
</html>

Related

Javascript and HTML (yes) - OpenWeatherMap API - how to search cities and get the data from them?

I'm trying to do a search engine (kind of), and am in need of help because I have some knowledge now of Javascript and HTML, but not this amount unfortunately.
This is all weather by the way, using OpenWeatherMap's free API.
Here's the HTML code:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name=""viewport" content=""width-device-width, initial-scale=1">
<title>My weather app</title>
</head>
<body>
<form>
<input type="text" id="cityName" placeholder="Enter a city name"><br>
<input type="submit" value="Get Weather Information" id="weather_data">
</form>
<div id="display_data"></div>
<h1 id=city_name"> Please wait...</h1>
<p id="myParagraph">Please wait...</p>
<img id="icon" src = "" alt = "Weather icon">
<p id="myTemp"> Please wait...</p>
<p id="minimum">Please wait...</p>
<p id="maximum">Please wait...</p>
<script src="City_Name.js"></script>
<script src="Data.js"></script>
</body>
</html>
This is the City_Name script:
// Fetch Wweather data from API and searching for a city
var weatherData = document.getElementById("weather_data");
weatherData.addEventListener('click',function(e){
e.preventDefault();
var cityName = document.getElementById("cityName").value;
var url = "https://api.openweathermap.org/data/2.5/weather?q="+cityName+"&appid={API Key}"
if(cityName == ""){
alert("Enter a city name");
}else{
fetch(url).then(function(response){
if(response.ok){
return response.json();
}else{
throw new Error(Error);
}
}).then(function(data){
console.log(data);
const html = `
<h2 class="text-danger text-center"><span class="text-dark">City:</span>${data.name}</h2>
` ;
document.getElementById("display_data").insertAdjacentHTML('afterbegin',html);
}).catch(function(error){
console.log(error);
});
}
});
And here is the Data script:
// Fetching City_Name to the script
include('City_Name.js')
// Fetch API
fetch('https://api.openweathermap.org/data/2.5/weather?q='+cityName+'&appid={API Key')
// Convert response string to json object
.then(response => response.json())
.then(response => {
// Display whole API response in browser console (take a look at it!)
console.log(response);
// Copy one element of response to our HTML paragraph
document.getElementById("myParagraph").innerHTML ="The weather is: " + response.weather[0].description;
document.getElementById("icon").src = "https://openweathermap.org/img/wn/" + response.weather[0].icon + "#2x.png";
document.getElementById("myTemp").innerHTML = "The temperature is: " + response.main.temp + " °C";
document.getElementById("minimum").innerHTML = "The minimum temperature should be: " + response.main.temp_min;
document.getElementById("maximum").innerHTML = "The minimum temperature should be: " + response.main.temp_max;
})
.catch(err => {
// Display errors in console
console.log(err);
});
The Data.js used to work fine with one city, like fetch('https://api.openweathermap.org/data/2.5/weather?q=Tokyo&appid={API Key}'), but now it doesn't.
Any help would be greatly appreciated.
Thanks!
To put it simply, you're going to need to have a single Javascript file for this to work. The City_Name.js.
After this line of code, document.getElementById("display_data").insertAdjacentHTML('afterbegin',html);, you don't need to have response.whateveritis. You're going to need data.whateveritis.
I hope that this answer will suit everyone's needs.
Thanks!
P.S.
Yes, I'm answering my own question.

change pagination number when scroll in url javascript

I have a problem. I want to make page like this
https://www.idntimes.com/korea/kpop/matthew-suharsono/rekomendasi-lagu-dreamcatcher-untuk-pengantar-tidur-c1c2/5
I already can add the page number at the end of the URL. But when I'm in testing.html/4 and I want to refresh it, the page does not appear and shows the error "Cannot get testing.html/4". How to make it can refresh like usual?
Here's my code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.spinner {
display: none;
}
</style>
</head>
<body style="font-size: 60px;">
<div class="news-content">
</div>
<div class="loading">
<p>Loading Please Wait</p>
</div>
<script>
function loadData(count) {
fetch('/index.json')
.then(res => res.json())
.then(json => {
if (count < json.length) {
let text = document.createElement('p');
text.innerText = json[count].text;
document.querySelector('.news-content').append(text);
if (count > 0) {
history.pushState(null, null, `/testing.html/${count}`)
}
}
});
}
let count = 0
window.addEventListener('load', loadData(count));
window.addEventListener('scroll', () => {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
count += 1;
loadData(count)
}
})
</script>
</body>
</html>
It seems to me that you are using pure HTML files in an HTTP/HTTPS local server. When you are having this kind of instance of the server you are not dynamically generating pages because you don't have any server side setup behind the HTML file.
You can do this using queries and since your app doesn't contain any server backend use client Javascript to create a pagination concept.
Instead of having a route type system ( which is usually handled by controller on the backend ) use query system:
Instead of:
/testing.html/{PAGE_NUMBER}
Use:
/testing.html?page={PAGE_NUMBER}
To get page query in Javascript, use the following function:
function getPageNumber() {
const urlParams = new URLSearchParams(window.location.search);
const page = urlParams.get('page');
return page;
}
Then create a function where you would paginate the data ( assuming the data is an array ):
function paginateData(data, resultsPerPage, pageNumber) {
// Chunk the data based on the limit
let result = data.reduce((rows, key, index) => (index % resultsPerPage == 0 ? rows.push([key]) : rows[rows.length-1].push(key)) && rows, []);
// Return the current page with index calculation
return result[pageNumber - 1];
}
And the final code should be something like this:
function getData(data) {
const RESULTS_PER_PAGE = 2;
const currentPageNumber = Number(getPageNumber());
const paginatedData = paginateData(data, RESULTS_PER_PAGE, currentPageNumber);
// If paginated data is undefined return first page
if (!paginatedData) {
/*
You can even redirect to /testing.html?page=1
*/
return paginateData(data, RESULTS_PER_PAGE, 1);
}
return paginatedData;
}
All you are left with is to provide the function getData with an data parameter resembling an array type.

TypeError: Cannot read properties of null (reading 'style') at handleClick

I want to change my target div variable's display property from none to block.
const userListEl = document.getElementById('user-list').innerHTML;
const template = Handlebars.compile(userListEl);
const targetDiv = document.getElementById("userDetail");
fetch("https://5dc588200bbd050014fb8ae1.mockapi.io/assessment")
.then(response => response.json())
.then((data) => {
var userData = template({ usersList: data })
document.getElementById('test').innerHTML = userData;
}).catch((error) => {
console.log(error)
})
this is my function
const hanldeClick = () => {
if (targetDiv.style.display === "none") {
targetDiv.style.display = "block";
} else {
targetDiv.style.display = "none";
}
};
#userDetail {
display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Exercise 1</title>
</head>
<body>
<div id="test"></div>
<script id="user-list" type="text/x-handlebars-template">
<ul class="people_list"> {{#each usersList}}
<li>
<p class="">{{name}}</p>
<img src={{avatar}} alt={{name}}>
<div id="userDetail">
<p>Id: {{id}}</p>
<p>Created at: {{createdAt}}</p>
</div>
<button onclick="hanldeClick()"> Detail </button>
</li>
{{/each}}
</ul>
</script>
<script type="text/javascript" src="app.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/handlebars#latest/dist/handlebars.js"></script>
</body>
I want to change the display value none to block in an onclick handler, but when the button is clicked I get this error:
TypeError: Cannot read properties of null (reading 'style')
Can someone help me?
targetDiv is null when you click the "Detail" button and trigger the handleClick function because there is no DOM element with the id userDetail when you invoke document.getElementById("userDetail");.
To verify this, you could log the value of targetDiv immediately after your assignment:
const targetDiv = document.getElementById("userDetail");
console.log(targetDiv); // null
Why is <div id="userDetail"> not a DOM element?
Because it is a part of a template within a script tag. The type="text/x-handlebars-template" attribute you have added to your script block is basically telling the browser to ignore what it contains. This is what allows you to add arbitrary content, like the mustaches understood by the Handlebars library. For more on this: see Explanation of <script type = "text/template"> ... </script>
In order for your code to reference the DOM element with id userDetail, you will need to get it from the DOM after you have injected your template-rendered HTML into document - ie., after you set the innerHTML of #test to userData:
let targetDiv = null;
fetch("https://5dc588200bbd050014fb8ae1.mockapi.io/assessment")
.then(response => response.json())
.then((data) => {
var userData = template({ usersList: data })
document.getElementById('test').innerHTML = userData;
targetDiv = document.getElementById("userDetail");
}).catch((error) => {
console.log(error)
})
I have created a fiddle for your reference.
Additionally, even with this fix, I think you are going to find that your code does not work as you intend. It looks like you want handleClick to toggle a sibling <div> - and that this should be the effect for each <li> in your <ul>. However, your document.getElementById("userDetail") will return only the first element with id userDetail, so no matter which "Detail" <button> is clicked, only the first <li>'s detail will be toggled. You will need to find a way to specify to your handler which detail to toggle.

Extract text with cheerio

I'm trying to write a script to extract email id and name from this website. I tried the following snippet but it doesn't work.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>foo</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div>
<strong style="color: darkgreen;">Can read this tag</strong>
<object id="external_page" type="text/html" data="https://aleenarais.com/buddy/" width="800px" height="600px"
style="overflow:auto;border:5px ridge blue">
<!-- I want to read tag values from this object -->
</object>
</div>
<script>
window.addEventListener('load', function () {
const item = [];
$('strong[style="color: darkgreen;"]').each(function () {
item.push($(this).text())
})
console.log(item)
})
</script>
</body>
</html>
Is there any better way to do this? Or is it possible to convert the whole page into a string and extract the email using RegEx?
The email and name of in the webpage are being rendered in an iframe. The source of iframe is an external source. In order for you to extract the information, you need to use a headless browser to do that.
I would suggest using Node.JS & Puppeteer (https://www.npmjs.com/package/puppeteer)
const puppeteer = require("puppeteer");
(async() => {
const url = "https://aleenarais.com/buddy/";
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url, {
waitUntil: "networkidle0"
});
var frames = await page.frames();
var myframe = frames.find(
(f) => f.url().indexOf("https://feedium.app/fetchh.php") > -1
);
const textFeed = await myframe.$$eval("strong", (sElements) =>
sElements.map((el) => el.textContent)
);
console.log(textFeed.splice(1)); //Array contains both name and email
await browser.close();
})();
Puppeteer loads the page similar to how a user loads the page. It waits until all the network calls are done (see network idle0) and then it tries finding the iframe which has the url (fetchh.php). If you observe, name and email are present in strong tags and they are the only strong tags available. Hence, we are extracting the strong tags, removing the count and we are left with just the name and email.
Output:
[ 'JJ', 'j*j#gmail.com' ] //I have just masked the values but the program gives the actual ones
Steps to run the script:
Install Node.Js (https://nodejs.org/en/download/)
Install puppeteer using (npm i puppeteer)
copy the script and place it in file (demo.js)
In the terminal, navigate to the directory in which the demo.js is
present and then run node demo.js
You should see the output.
Try this:
window.addEventListener('load', function () {
let item = [];
$('strong[style*="color: darkgreen;"]').each(function (index, item) {
item.push($(this).text())
})
console.log(item)
}

Without clicking the button to fetch data in js, my page fetched data automatically, and there is the second call to button [duplicate]

This question already has answers here:
addEventListener Executing Before Being Clicked
(2 answers)
Closed 2 years ago.
Can anyone help me with that- I am using fetch api here and this is linked to a button ,here I used fetch api for a get request, but the problem is that without clicking the button ,my data is fetched from the api.
When I clicked the button to fetch data first time, it works perfectly but after that on reload my data is fetched automatically without clicking button. what's the problem here and how to fix it?
easyhttp.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>easy http</title>
</head>
<body>
<ol id="getRequestData">
</ol>
<button id="btn1">get</button>
<button id="btn2">post</button>
<button id="btn3">put</button>
<button id="btn4">delete</button>
<script src="easyHttpWithFetch.js"></script>
</body>
</html>
easyHttpWithFetch.js
document.getElementById("btn1").addEventListener("click",get("https://jsonplaceholder.typicode.com/posts"));
function get(url){
fetch(url)
.then(response => response.json())
.then((data) => {
let str = "";
data.forEach(element => {
str += '<li><ol type="a">';
for (const key in element) {
if (Object.hasOwnProperty.call(element, key)) {
const value = element[key];
str+= `<li>${value}</li>`;
}
}
str += '</ol></li>';
let getRequestData = document.getElementById("getRequestData");
getRequestData.innerHTML = str;
});
}).catch((err) => {
console.log(err);
});
}
The second parameter of the addEventListener() is the function name that we want to call when the click occurs. But you are currently trying to execute the get() method by passing the url parameter immediately.
That's why get() is first called initially when btn1 is attached to the click event.
To fix this, try to use the arrow function.
document.getElementById("btn1").addEventListener("click", () => get("https://jsonplaceholder.typicode.com/posts"));
function get(url) {
fetch(url)
.then(response => response.json())
.then((data) => {
let str = "";
data.forEach(element => {
str += '<li><ol type="a">';
for (const key in element) {
if (Object.hasOwnProperty.call(element, key)) {
const value = element[key];
str += `<li>${value}</li>`;
}
}
str += '</ol></li>';
let getRequestData = document.getElementById("getRequestData");
getRequestData.innerHTML = str;
});
}).catch((err) => {
console.log(err);
});
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>easy http</title>
</head>
<body>
<ol id="getRequestData">
</ol>
<button id="btn1">get</button>
<button id="btn2">post</button>
<button id="btn3">put</button>
<button id="btn4">delete</button>
</body>
</html>

Categories