How to make script execute once the first script is loaded - javascript

I'm trying to make a web framework and one feature will be a key-value state management tool. I need the second <script> tag to only run after ./script.js loads in.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./framework.js"></script>
</head>
<body>
<p f-text="name"></p>
<script>
Framework.store('name', 'Joe');
</script>
</body>
</html>
framework.js:
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
const key = window.fStore.find((x) => x.key === textValue);
element.innerHTML = key.value;
}
});
window.Framework = {
store: (key, value?) => {
if (!value) {
const foundKey = window.fStore.find((x) => x.key === key);
return foundKey.value;
}
window.fStore = [...window.fStore, { key: key, value: value }];
}
}
Error:
SyntaxError: Unexpected token ')'
at /framework.js:12:22
ReferenceError: Framework is not defined
at /:12:5

You need to wait that your script is loaded, you can use this
window.addEventListener('load', function() {
Framework.store('name', 'Joe');
})

Related

Javascript Dynamic Data binding code not working

I am writing code that uses data binding to change the innerHTML of an span to the input of the user, but I can't get it to work. What it should do is show the input on the right side of the input field on both the input fields, but it doesn't. Can someone please help me out.
HTML:
<html lang="en-US">
<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>My Frontend Framework</title>
</style>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" bit-data="name"/>
<span bit-data-binding="name" style="margin-left: 1rem;"></span>
</div>
<div>
<label>Lastname:</label>
<input type="text" bit-data="LastName"/>
<span bit-data-binding="LastName" style="margin-left: 1rem;"></span>
</div>
<script src="frontend-framework.js"></script>
</body>
</html>
Javascript:
const createState = (stateObj) => {
return new Proxy(stateObj, {
set(target, property, value) {
target[property] = value;
render();
return true;
}
});
};
const state = createState({
name: '',
lastName: ''
});
const listeners = document.querySelectorAll('[bit-data]');
listeners.forEach((element) => {
const name = element.dataset.model;
element.addEventListener('keyup', (event) => {
state[name] = element.value;
console.log(state);
});
});
const render = () => {
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map(
e => e.dataset.binding
);
bindings.forEach((binding) => {
document.querySelector(`[bit-data-binding=${binding}]`).innerHTML = state[binding];
document.querySelector(`[bit-data=${binding}]`).value = state[binding];
});
}
https://jsfiddle.net/Mauro0294/g3170whc/4/
I made some changes to the fiddle to get the desired result. The problem was with your logic to refer the elements using the dataset attributes, so I tried to simplify it.
Some notable changes :
Updated the data-bit to use lastName instead of LastName. Made it same as your state.
Used getAttribute to get the value of the data-* properties to correctly get the reference.
I think this is what you're looking for:
const createState = (stateObj) => {
return new Proxy(stateObj, {
set(target, property, value) {
target[property] = value;
render();
return true;
}
});
};
const state = createState({
name: '',
lastName: ''
});
const listeners = document.querySelectorAll('[bit-data]');
listeners.forEach((element) => {
const name = element.getAttribute('bit-data');
console.log('here', element.getAttribute('bit-data'), JSON.stringify(element.dataset))
element.addEventListener('keyup', (event) => {
state[name] = element.value;
console.log(state);
});
});
const render = () => {
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map((e) => {
return e.getAttribute('bit-data-binding');
});
//console.log('bindings:', bindings, document.querySelectorAll('[bit-data-binding]'));
(bindings ?? []).forEach((binding) => {
document.querySelector(`[bit-data-binding=${binding}]`).innerHTML = state[binding];
document.querySelector(`[bit-data=${binding}]`).value = state[binding];
});
}
<html lang="en-US">
<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>My Frontend Framework</title>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" bit-data="name"/>
<span bit-data-binding="name" style="margin-left: 1rem;"></span>
</div>
<div>
<label>Lastname:</label>
<input type="text" bit-data="lastName"/>
<span bit-data-binding="lastName" style="margin-left: 1rem;"></span>
</div>
</body>
</html>
Your main issue is this part:
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map(
e => e.dataset.binding
);
or more specifically e.dataset.binding. Your elements do not a have data-binding attribute, which would be the prerequisite for using dataset.binding. You can use e.getAttribute('bit-data-binding') instead.
But your logic is also flawed: As it currently stands, entering text into an input is pointless, as the state is never updated.
Finally, note that you spell LastName with a capital L in your DOM but lowercased in your state object.

Cannot set properties of undefined in key-value store

I have this code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./framework.js"></script>
</head>
<body>
<p f-text="name"></p>
<script>
Framework.store('name', 'Joe');
</script>
</body>
</html>
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
element.innerHTML = window.fStore[textValue];
}
});
window.Framework = {
store: (key, value = '') => {
if (value === '') {
return window.fStore[key];
}
window.fStore[key] = value;
}
}
But get this error:
TypeError: Cannot set properties of undefined (setting 'name')
at Object.store (/framework.js:15:24)
at /:12:15
I want the page to render 'Joe' by getting the key from f-text, finding the key's value in window.fStore, then setting the element.innerHTML as the value. Framework.store() takes a key and a value, if there is no value it returns the value from the key in window.fStore, if there is then it sets window.fStore[key] to the value.
You need to check whether window.fStore exists first.
window.Framework = {
store: (key, value = '') => {
if(!window.fStore) window.fStore = {}
if (value === '') {
return window.fStore[key];
}
window.fStore[key] = value;
}
}
Framework.store('name', 'Joe');
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
element.innerHTML = window.fStore[textValue];
}
});
<!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>
<p f-text="name"></p>
</body>
</html>
You might also need to wait till the window loads first. Some browsers will give you a headache if you dont
window.addEventListener('load', e=>{
window.Framework = {
store: (key, value = '') => {
if(!window.fStore)
window.fStore = {};
if (value === '')
return window.fStore[key];
window.fStore[key] = value;
}
}
window.Framework.store('name', 'Joe');
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text'))
element.innerHTML = window.fStore[element.getAttribute('f-text')];
});
}

How to filter object array on javascript

im new in js
I want to make a code with onekeyup to search an object in an array. I don't know how to do it with one/specific parameter, not all of the parameter.
for ex. If I type tokyo, then it will not show anything because its not "name" parameter
script.js and index.html
function checkOnKeyUp(input){
const filtered = datas.filter(element => {
for (const value of Object.values(element)) {
if (
value.toString()
.toLowerCase()
.includes(input.value.toLowerCase())
)
return true;
}
})
console.log('Name: ', filtered);
document.getElementById("result").innerText=JSON.stringify(filtered)
}
const datas = [{
"nickname":"abi","name":"Abi sholeh","id":123,"birth":"1999-05-09","address":"new york"},{"nickname":"abc","name":"abc james","id":112,"birth":"1999-05-04","address":"tokyo"}];
<!DOCTYPE html>
<html lang="en">
<head>
<title>Pengenalan Javascript</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<input type="text" id='search' name='search' onkeyup="checkOnKeyUp(this)">
<div id="result"></div>
<script src="./script.js"></script>
</body>
</html>
thanks
I think you are trying to the follow:
datas.map(data => Object.values(data))
.flat()
.filter(value =>
value.toString()
.toLowerCase()
.includes(input.value.toLowerCase()
)
.forEach(data => {
data = 'nama';
});
Maybe this is what you are looking for:
function checkOnKeyUp(input){
let value = input.value.toLowerCase();
let filtered = datas.filter(element => {
return element.name.toLowerCase() === value;
})
console.log('Name: ', filtered.name);
document.getElementById("result").innerText=JSON.stringify(filtered)
}

Javascript callback called twice

I'm pretty new with coding, and this is really stumping me...
Here is my index.html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
<link rel="stylesheet" href="owlcarousel/owl.carousel.css">
<link rel="stylesheet" href="owlcarousel/owl.theme.default.css">
</head>
<body>
<div class="owl-carousel owl-theme">
</div>
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous" type="text/javascript" language="JavaScript"></script>
<script src="jquery.min.js"></script>
<script src="owlcarousel/owl.carousel.js"></script>
<script src="app.js"></script>
<script>
fetch('https://www.paulschlatter.com/slideshow/slideshows.txt')
.then((response) => response.text().then(yourCallback));
let cache = {}
function yourCallback(retrievedText, callback) {
if (cache[retrievedText]) {
console.log('oops')
} else {
let array = []
console.log(callback)
array = retrievedText.split(/\n|\r/g)
let httpsArray = []
let keysArray = []
let mappedArray = array.map(item => {
if (item.substring(0, 5) === 'https') {
httpsArray.push(item)
} else if (item.substring(0, 3) === '202') {
keysArray.push(item)
}
})
const object = { ...keysArray
}
for (const prop in object) {
window['value' + prop] = []
httpsArray.filter(item => item.includes(object[prop])).map(item => {
window['value' + prop].push(item)
})
}
const owlImages = document.querySelector('.owl-carousel'),
owlDiv = document.createElement('img');
owlDiv.setAttribute('src', window.value0.pop())
owlDiv.setAttribute('alt', '')
owlImages.appendChild(owlDiv)
}
}
</script>
</body>
</html>
I am not using npm or anything, just straight JavaScript, and HTML.
The function yourCallback is firing twice, so even when I only console.log hello world it returns hello world twice to my browser.
Obviously this is not ideal, and I believe that the problem lies in the
fetch('https://www.paulschlatter.com/slideshow/slideshows.txt')
.then((response) => response.text().ten(yourCallback));
This was a silly mistake, in my app.js file I had the same fetch and yourCallback function, so it was firing twice cause I called it twice :)

Why won't the contents of the API be displayed?

I'm trying to display contents of an API but for some reason I'm getting an error in the console that says Uncaught (in promise) TypeError: Cannot read property 'appendChild' of null which sort doesn't make sense to me because I've set to innerHTML = <p>${this.items[i]}</p>.
What am I doing wrong and how can I fix this? If you need more information, please let me know.
Here's my html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="baseball">
<h1>test</h1>
</div>
<script type="application/javascript" src="index.js"></script>
<script type="text/javascript">
window.addEventListener("load", function() {
console.log("document is loaded");
var baseballStats = new BaseballStats();
baseballStats.init("https://statsapi.mlb.com/api/v1/people/660670/stats?stats=byDateRange&season=2018&group=hitting&startDate=&endDate=&leagueListId=mlb_milb", true);
});
</script>
</body>
</html>
Here's my javascript
class BaseballStats {
constructor() {
this.totalItems = 0;
this.list = document.querySelector("baseball");
}
init(url, bool) {
this.bool = bool;
var that = this;
console.log(url);
fetch(url)
.then(resp => resp.json())
.then(data => {
console.log(data.stats);
that.data = data;
if (this.bool) {
that.items = that.data.stats;
this.totalItems = that.items.length;
console.log("about to loop");
for (var i = 0; i < this.totalItems; i++) {
var listNode = document.createElement("LI");
listNode.innerHTML = `<p>${this.items[i]}</p>`;
console.log("did it reach here");
this.list.appendChild(listNode);
}
}
});
}
}
Try to console.log the list variable. You will see it's null. You're treating this variable as an object, but the content of the variable is null.
Your problem is the BaseballStats' list member is null. This is because you're misusing document.querySelector - it selects in the same way as CSS, so to select an element with a class you need to use the . selector - https://www.w3schools.com/cssref/css_selectors.asp.
You are selecting for 'baseball' however, which means it is trying to find an element with the tag name <baseball>. Change this to '.baseball' and it will work

Categories

Resources