I have a similar class structure and it doesn't work for me, I've already tried several things and can't fix the problem. As you can see, the constructors are executed correctly and also the method executed in the last constructor. However, when I create HTML content, it doesn't paint it. Why and how could you solve this?
class AutoComplete{
constructor(){
console.log("constructor autocomplete")
this.table = new Table();
}
}
class Table{
constructor(){
console.log("constructor table")
this.arr = []
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((data) => {
data.map(d => this.arr.push(d))
});
this.fill();
}
fill = () => {
console.log("fill");
const content = document.querySelector("#content");
// doesn't work
this.arr.forEach( ct => {
const div = document.createElement("div");
div.innerText = ct.body;
content.appendChild(div);
//content.innerHTML += div;
});
}
}
let autoc = new AutoComplete();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title></title>
</head>
<body>
<div id="content"></div>
</body>
</html>
This is happening because you need to call this.fill() within the .then() callback function. Otherwise. this.fill is called before you get data back from the API.
Demo:
class AutoComplete{
constructor(){
console.log("constructor autocomplete")
this.table = new Table();
}
}
class Table{
constructor(){
console.log("constructor table")
this.arr = []
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((data) => {
data.map(d => this.arr.push(d));
this.fill();
})
// this.fill()
}
fill = () => {
console.log("fill");
const content = document.querySelector("#content");
// doesn't work
this.arr.forEach(ct => {
const div = document.createElement("div");
div.innerText = ct.body;
content.appendChild(div);
//content.innerHTML += div;
});
}
}
let autoc = new AutoComplete();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title></title>
</head>
<body>
<div id="content"></div>
</body>
</html>
Related
I, not so long ago, went ahead and built an html dependent dropdown which pulls it's data from an array in the js. The dependencies worked perfectly fine until I realized that I needed to add a search function to the dropdown.
I went through different alternatives and to me the simplest option was to use select2 plugin. The problem I am having is that when using select2, it doesn't seem to be triggering the EventListener (Line 43 in JS) I had previously setup for the regular select.
Find below what I have attempted:
HTML:
<!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">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2#4.0.13/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2#4.0.13/dist/js/select2.min.js"></script>
<title>Document</title>
</head>
<body>
<select id ="level1" style='width: 300px;'></select>
<select id ="level2" style='width: 300px;'></select>
<select id ="level3" style='width: 300px;'></select>
<hr>
<select id ="level4" disabled></select>
<select id ="level5" disabled></select>
<select id ="level6" disabled></select>
<select id ="level7" disabled></select>
<hr>
<h1 id ="level8"></h1>
<script src="betterdd.js"></script>
</body>
</html>
JS: (Select options are found in var myData = [...])
class DropDown {
constructor(data){
this.data = data;
this.targets = [];
}
filterData(filtersAsArray){
return this.data.filter(r => filtersAsArray.every((item,i) => item === r[i]));
}
getUniqueValues(dataAsArray,index){
const uniqueOptions = new Set();
dataAsArray.forEach(r => uniqueOptions.add(r[index]));
return [...uniqueOptions];
}
populateDropDown(el,listAsArray){
el.innerHTML = "";
listAsArray.forEach(item => {
const option = document.createElement("option");
option.textContent = item;
el.appendChild(option);
});
}
createPopulateDropDownFunction(el,elsDependsOn){
return () => {
const elsDependsOnValues = elsDependsOn.length === 0 ? null : elsDependsOn.map(depEl => depEl.value);
const dataToUse = elsDependsOn.length === 0 ? this.data : this.filterData (elsDependsOnValues);
const listToUse = this.getUniqueValues(dataToUse, elsDependsOn.length);
this.populateDropDown(el,listToUse);
}
}
add(options){
//{target: "level2", dependsOn: ["level1"] }
const el = document.getElementById(options.target);
const elsDependsOn = options.dependsOn.length === 0 ? [] : options.dependsOn.map(id => document.getElementById(id));
const eventFunction = this.createPopulateDropDownFunction (el, elsDependsOn);
const targetObject = { el: el, elsDependsOn: elsDependsOn,func: eventFunction};
targetObject.elsDependsOn.forEach(depEl => depEl.addEventListener("change",eventFunction));
this.targets.push(targetObject);
return this;
}
initialize(){
this.targets.forEach(t => t.func());
return this;
}
eazyDropDown(arrayOfIds){
arrayOfIds.forEach((item,i) =>{
const option = {target: item, dependsOn: arrayOfIds.slice(0,i) }
this.add(option);
});
this.initialize();
return this;
}
}
var dd = new DropDown(myData).eazyDropDown(["level1","level2","level3","level4","level5","level6","level7","level8"])
add the following line inside add method :
const eventFunction = this.createPopulateDropDownFunction (el, elsDependsOn);
el.addEventListener("change", (e) => {
eventFunction();
console.log(e.target.value)
})
and remove the following line:
targetObject.elsDependsOn.forEach(depEl => depEl.addEventListener("change",eventFunction));
I got a task to render a word using pure JavaScript and modules, but always got mistakes like params of renderDOM function is undefined and so on. I'm able to choose the order of scripts, use IIFE
here is html:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div class="root"></div>
<script src="invert.js"></script>
<script>
window.render.renderDOM('.root', reverse('sseccus'));
</script>
<script src="dom.js"></script>
</body>
</html>
and 3 files with functions:
dom.js
const TAG = 'div';
function createElement(tag = TAG, content) {
const element = document.createElement(tag);
element.textContent = content;
return element;
}
render.js
const TAG = 'p';
function renderDOM(selector, content) {
const root = document.querySelector(selector);
if (!root) {
return;
}
const element = createElement(TAG, content); // createElement из файла dom.js
root.appendChild(element);
}
reverse.js
(function () {
function reverse(str) {
return str.split('').reverse().join('');
}
})();
I've tried to add type='module', added export or export default to the functions. As a result there must be "success" rendered.
index.html
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div class="root"></div>
<script src="invert.js"></script>
<script src="dom.js"></script>
<script src="render.js"></script>
<script src="reverse.js"></script>
<script>
window.render.renderDOM('.root', reverse('sseccus'));
</script>
</body>
</html>
render.js
const TAG = 'p';
function renderDOM(selector, content) {
const root = document.querySelector(selector);
if (!root) {
return;
}
const element = createElement(TAG, content);
root.appendChild(element);
}
window.render = {renderDOM};
dom.js
const createElement = (() => {
const TAG = 'div';
return function createElement(tag = TAG, content) {
const element = document.createElement(tag);
element.textContent = content;
return element;
}
})();
I'm trying to display all elements of an array, by iterating through the arrray, but after I chose the file (from the input), the element in page changes to : "unidentified". Why?
function getElement() {
console.log('sfgsdf')
document.getElementById('files').onchange = function() {
console.log('sfgsdf')
let file = this.files[0];
let reader = new FileReader();
reader.readAsText(file);
reader.onload = function() {
variableIs = this.result
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
function display(asd) {
const usingSplit = asd.split(' ')
lengthOf = usingSplit.length
for (var i = 0;i < lengthOf;i++) {
sleep(1000).then(() => {
document.getElementById('test').innerHTML = usingSplit[i];
});
}
}
display(variableIs);
}
}
}
getElement()
The HTML code is just this simple one :
<!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="test">TEST</h1>
<script src="test4.js"></script>
</body>
</html>
The problem is the var inside the for loop.
You should use let instead due to the way both work.
Read this post to understand their difference:
What is the difference between "let" and "var"?
The way you're using the sleep function is not gonna work since they will execute at the same time.
You can solve this by using async await.
function delay(ms = 0) {
return new Promise(resolve => setTimeout(resolve, ms))
}
const inputFile = document.getElementById('files')
const test = document.getElementById('test')
function init() {
inputFile.onchange = async function() {
const file = this.files[0];
const content = await file.text()
const splitText = content.split(' ')
for (let i = 0; i < splitText.length; i++) {
test.innerHTML = splitText[i]
// Here we are actually waiting in the for loop since this is an async function
await delay(1000)
}
}
}
init()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 id="test">TEST</h1>
<input id="files" type="file">
</body>
</html>
The button should replace all the letters (a, b, c) with 'test', but it only replaces the last (c).
If I replace the line of code inside of updateHTML() with the commented line, the code works as intended. Can someone explain what the difference is and why the lines of code work differently.
My js and html files:
let names = ["a", "b", "c"];
let objs = [];
let container;
let form;
class Class {
constructor(name) {
this.name = name;
this.buildHTML();
}
buildHTML() {
container.innerHTML += `
<div id="${this.name}">
${this.name}
</div>`
this.inner_container = document.getElementById(this.name);
}
updateHTML() {
this.inner_container.innerHTML = "test";
// document.getElementById(this.name).innerHTML = "test";
}
}
function main() {
container = document.getElementById("container");
for (var name in names) {
objs.push(new Class(names[name]));
}
form = document.getElementById("form");
form.addEventListener('submit', eventHandler_submit_form);
}
function eventHandler_submit_form(event) {
event.preventDefault();
for (var obj in objs) {
objs[obj].updateHTML();
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Sample Text</title>
</head>
<body onload="main()">
<form id="form">
<button type="submit">Submit</button>
</form>
<div id="container"></div>
</body>
</html>
I am trying to get the return results (title, content, poet) to show up on the HTML. Right now, only the content is changing dynamically whenever I refresh the page. I'm not sure what I am doing wrong in .then() - can you put multiple document.getElementId in there, or is there another way to structure it?
Note: I commented the other document.getElementId and document.getElementsByTagName because they just ended up showing the entire content 2 times
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel = "stylesheet" href = "style.css">
<title>Fetch a poem</title>
</head>
<body>
<h1 class = "title">Title</h1>
<h3 id = "content">Fetch a Poem</h3>
<p id = "poet">Poet</p>
<script>
console.log('about to fetch a poem');
catchPoem()
.then(poem => {
// document.getElementsByClassName('title').innerText = poem;
document.getElementById('content').innerText = poem;
// document.getElementById('poet').innerText = poem;
console.log('content is showing');
})
.catch(error => {
console.log('error!');
console.error(error);
});
async function catchPoem() {
const response = await fetch('https://www.poemist.com/api/v1/randompoems');
let json = await response.json();
let title = json[0].title
let content = json[0].content
let poet = json[0].poet.name
console.log(title)
console.log(content)
console.log(poet)
return [title, content, poet]
}
</script>
</body>
</html>
You need to set all three in the .then block
document.querySelector( '.title' ).textContent = title;
document.getElementById( 'content' ).textContent = content;
document.getElementById( 'poet' ).textContent = poet;
I've used textContent here, You can use innerText
console.log('about to fetch a poem');
catchPoem()
.then(([title, content, poet]) => {
// document.getElementsByClassName('title').innerText = poem;
document.querySelector('.title').textContent = title;
document.getElementById('content').textContent = content;
document.getElementById('poet').textContent = poet;
// document.getElementById('poet').innerText = poem;
console.log('content is showing');
})
.catch(error => {
console.log('error!');
console.error(error);
});
async function catchPoem() {
const response = await fetch('https://www.poemist.com/api/v1/randompoems');
let json = await response.json();
let title = json[0].title
let content = json[0].content
let poet = json[0].poet.name
return [title, content, poet]
}
h1, p{
text-align: center;
}
h1, h3, p{
padding: .5rem 1rem;
border-radius: 4px;
}
.title{
background-color: cadetblue;
}
#content{
background-color: chocolate;
}
#poet{
background-color: yellow;
}
<h1 class="title">Title</h1>
<h3 id="content">Fetch a Poem</h3>
<p id="poet">Poet</p>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel = "stylesheet" href = "style.css">
<title>Fetch a poem</title>
</head>
<body>
<h1 id = "title">Title</h1>
<h3 id = "content">Fetch a Poem</h3>
<p id = "poet">Poet</p>
<script>
console.log('about to fetch a poem');
catchPoem()
.then(poem => {
document.getElementById('title').innerText = poem[0];
document.getElementById('content').innerText = poem[1];
document.getElementById('poet').innerText = poem[2];
console.log('content is showing');
})
.catch(error => {
console.log('error!');
console.error(error);
});
async function catchPoem() {
const response = await fetch('https://www.poemist.com/api/v1/randompoems');
let json = await response.json();
let title = json[0].title
let content = json[0].content
let poet = json[0].poet.name
console.log(title)
console.log(content)
console.log(poet)
return [title, content, poet]
}
</script>
</body>
</html>
Another Way Of Generalization
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="style.css" />
<title>Fetch a poem</title>
</head>
<body>
<div id="content">
<p id="loader">Loading Poems ....</p>
</div>
<script>
console.log("about to fetch a poem");
catchPoem()
.then((poems) => {
let div = document.getElementById("content");
div.removeChild(document.getElementById("loader"));
for (let poem of poems) {
let title = document.createElement("h1");
title.innerHTML = poem.title || "No Title";
div.appendChild(title);
let poemTag = document.createElement("h3");
poemTag.innerHTML = poem.content || "No Poem";
div.appendChild(poemTag);
let poet = document.createElement("p");
poet.innerHTML = poem.poet.name || "No Poet";
div.appendChild(poet);
}
})
.catch((error) => {
console.log("error!" + error);
});
async function catchPoem() {
const response = await fetch("https://www.poemist.com/api/v1/randompoems");
let json = await response.json();
return json;
}
</script>
</body>
</html>