I'm still new to javascript, I have this javascript problem from CS50 that is supposed to open a mailbox and clicking on an email is supposed to open the email. I think my on click part of the problem is right, but when I open my page and click on an email it doesnt call the open_mail() function.
I've solved that the problem is that the load_mailbox function for being asynchronous is beign called after the DOM finishes to load, so technically theres no div with the class email-box when the DOM finishes to load, but i don't know how to solve this problem, can someone help please.
document.addEventListener('DOMContentLoaded', function() {
// Use buttons to toggle between views
document.querySelector('#inbox').addEventListener('click', () => load_mailbox('inbox'));
document.querySelector('#sent').addEventListener('click', () => load_mailbox('sent'));
document.querySelector('#archived').addEventListener('click', () => load_mailbox('archive'));
document.querySelector('#compose').addEventListener('click', compose_email);
document.querySelector('#compose-form').addEventListener('submit', send_mail);
document.querySelectorAll('.email-box').forEach(function(box) {
box.addEventListener('click', function (){
open_mail();
})
});
// By default, load the inbox
load_mailbox('inbox');
});
function load_mailbox(mailbox) {
fetch(`/emails/${mailbox}`)
.then(response => response.json())
.then(emails => {
document.querySelector('#email-content').innerHTML = "";
emails.forEach(inbox_mail);
})
};
function inbox_mail(email) {
const element = document.createElement('div');
if (document.querySelector(`#email-${email.id}`) === null) {
element.id = (`email-${email.id}`);
element.className = ("email-box");
element.innerHTML = `<p>From ${email.sender}</p><p>${email.subject}</p><p>At ${email.timestamp}
</p>`;
document.querySelector('#email-content').append(element);
}
}
I´d say the easiest solution would be to put the addEventListener to a point after the elements with class .email-box are created, e.g in your .then function after inbox_mail ran for each email
.then(emails => {
document.querySelector('#email-content').innerHTML = "";
emails.forEach(inbox_mail);
document.querySelectorAll('.email-box').forEach(function(box) {
box.addEventListener('click', function (){
open_mail();
});
});
});
DOMContentLoaded will trigger when the DOM from the initial request/response was loaded. What you are doing in your fetch callback is called "DOM-Manipulation" as you create elements and append them to the DOM that has already been loaded.
Related
I try to console some message when image is fully loaded using 'load' listener, but the code did not work, how to properly write a 'load' event listener in react ? Thankyou
useEffect(() => {
window.addEventListener('load', (event) => {
const imageTest = document.querySelector('img')
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded)
})
}, [])
This is not how react works. You are trying to use load event within the component when everything else is already loaded within from <div id="root"></div>.
React is Single Page App. And for the whole document load happens once only :)
However for individual elements we can set onload and fire that event in componentDidMount() or in useEffect() Hook
UPDATE: For image load check you do something like. You can do this or even use useRef()
useEffect(() => {
const imageTest = document.querySelector('img');
imageTest.onload = ()=>{
// Image is loaded and your further steps :)
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded);
}
}, []);
There is also one more easy way to do this:
Using onLoad synthetic event right on the image element itself. Which I think should also work fine:
const ImageLoadDemo ()=> {
const handleImageLoaded =()=> {
console.log("Image was successfully loaded");
}
const handleImageErrored =()=> {
console.log("Image was not loaded successfully");
}
return (
<div>
<img
src="https://picsum.photos/200/300"
onLoad={handleImageLoaded}
onError={handleImageErrored}
/>
</div>
);
}
The way to listen image is successfully loaded in react component is just put onLoad on your <img> tag, for example :
const MyCompoent = () => {
return <img src="yourImageLink.png" onLoad={()=>{console.log('The Image is successfully loaded')} } />
}
instead console a message you can pass a function as well
I have strange problem with buttons that are requesting for displaying templates on client page.
This is client side code. The main task of entire class is to just enable user to click button, send request and get response with HTML that has been rendered from handlebars template and just paste it in partiuclar place on client side. It works, but only once. After first click and displaying elements, I totally lose any interaction with those buttons. There is no request, and there is no even EventListener for clicking. I get no error. Completely there is no single reaction after clicking.
class Weapons {
constructor() {
this.buttons = document.querySelectorAll('.type')
}
async displayWeapon(path) {
const container = document.querySelector('.shop-container')
await fetch(`weapons/${path}`).then(response => response.json()).then(data => container.innerHTML += data);
}
chooseWeapon() {
this.buttons.forEach(btn => {
btn.addEventListener('click', (e) => {
console.log('click');
let weaponType = e.target.dataset.type
switch (weaponType) {
case 'pistols':
console.log('click');
return this.displayWeapon(weaponType)
case 'rifles':
console.log('click');
return this.displayWeapon(weaponType)
case 'grenades':
console.log('click');
return this.displayWeapon(weaponType)
case 'closerange':
console.log('click');
return this.displayWeapon(weaponType)
case 'rocketlauchner':
console.log('click');
return this.displayWeapon(weaponType)
}
})
})
}
}
document.addEventListener('DOMContentLoaded', function () {
const weapons = new Weapons();
weapons.chooseWeapon();
> When I invoke displayWeapon(path) here it also works, but immidiately
> after displaying html elements clicking on buttons again does not
> initiate any action.
})
Here is app.get function but I doubt it's source of problem.
app.get('/weapons/:id', (req, res) => {
console.log('req');
console.log(req.url);
let type = req.params.id;
res.render(type, function (err, html) {
res.json(html);
})
})
Ok. The answer is actually simple. In fetch function container.innerHTML += data. This line deletes my html with buttons, and the same time it deletes eventListeners. So I need just to modify my html.
I have a page that includes a third party script (Xsolla login). This script modifies elements on the page, one of the particular elements being iframe.
First a placeholder element is inserted
Then the iframe is deleted and new iframe is inserted with different dynamically loading content
Both iframes have the same id
How one can detect when the second, replaced, iframe is correctly loaded as Cypress cy.get() grabs the first iframe and then never detects newly changed content within the replaced iframe?
You can use cypress-wait-until plugin and then write a custom check function that inspects deep into the iframe.
/**
* Because Xsolla does dynamic replacement of iframe element, we need to use this hacky wait.
*/
function checkLoadedXsollaIframe(doc: Document): bool {
try {
const iframe = doc.getElementById('XsollaLoginWidgetIframe') as any;
if(!iframe) {
return false;
}
// This element becomes available only when Xsolla has done its magic JS loading
const usernameInput = iframe.contentDocument.querySelector('input[name="email"]');
return usernameInput !== null;
} catch(e) {
return false;
}
}
context('Login', () => {
beforeEach(() => {
cy.visit(frontendURL);
});
it('Should login with valid credentials', () => {
// This injects Xsolla <script> tag and then
// this third party script takes its own course of actions
cy.get('.login-btn').first().click();
cy.waitUntil(() => cy.document().then(doc => checkLoadedXsollaIframe(doc)), {timeout: 5000 });
Below is the snippet that waits for the content inside the iframe to be loaded and HTMLElements be available & no timeouts required.
const iframeElement = (selector) => {
const iframe = cy.get(selector);
return iframe
.should(($iframe) => // Make sure its not blank
expect($iframe.attr('src')).not.to.include('about:blank')
)
.should(($iframe) =>
expect($iframe.attr('src')).not.to.be.empty) // Make sure its not empty
.then(($inner) => {
const iWindow = $inner[0].contentWindow;
return new Promise((resolve) => {
resolve(iWindow);
});
})
.then((iWindow) => {
return new Promise((resolve) => {
iWindow.onload = () => { // Listen to onLoad event
resolve(iWindow.document);
};
});
})
.then((iDoc) => {
return cy.wrap(iDoc.body); // Wrap the element to access Cypress API
});
};
Now access the element inside the iframeDocument
iframeElement('#my-iframe') // Grab the iframe
.find('h2')
.should('have.text', 'My header text'); //Assert iframe header
Note: Don't attempt to access CORS websites. It might fail due to
security reasons
I open a tab from JavaScript and try to track when it is closed. But none of the events is fired. I only find examples with onbeforeunload referencing the current window, not other window-objects.
window.addEventListener('DOMContentLoaded', () => {
document.querySelector('#youtube-open').addEventListener('click', () => {
document.yTT = window.open('https://youtube.com', '_blank');
document.yTT.addEventListener('close', () => {
console.log('onclose fired');
});
document.yTT.addEventListener('beforeunload', () => {
console.log('onbeforeunload fired');
});
document.yTT.addEventListener('unload', () => {
console.log('onunload fired');
});
});
});
There are no errors in the JS console or something. It just doesn't work. Any ideas why?
you can not access the other window instances of a browser. A way to bypass that is to use the local storage(which can be accessed from the events you listed above), to store some cross-tab data and a polling mechanism(in your main tab) to get the states.
Yes it is possible to track when the window is closed just not that way.
Your document.yTT variable holds the WindowProxy object returned by window.open(). That object will not emit the events you are trying to listen to unfortunately. It will however hold a property that says if the window has been closed.
You can do this if you want to call a function when the window is closed :
window.addEventListener('DOMContentLoaded', () => {
document.querySelector('#youtube-open').addEventListener('click', () => {
document.yTT = window.open('https://youtube.com', '_blank');
document.yTTInterval = setInterval(() => {
if (document.yTT.closed) {
clearInterval(document.yTTInterval);
executeSomeFunction();
}
}, 1); // or whatever interval works for you, the longer the less consuming
});
});
I'm really new to javascript and jquery and I'm trying to get a button to autoclick from an existing code base. I am having trouble getting the button to do that even though this is what I've been following How to auto click an input button.
The code is below. I've tried using both .submit() and .click() but the start-game-btn doesn't seem to get pressed either way. I've put a console.log within the button whenever it's pressed and the message doesn't seem to get logged.
console.log("loading...")
$(() => {
$("#start-game-btn").click(event => {
console.log("start-game")
$("#errors").text("")
event.preventDefault()
const height = parseInt($("#height").val())
const width = parseInt($("#width").val())
const food = parseInt($("#food").val())
let MaxTurnsToNextFoodSpawn = 0
if ($("#food-spawn-chance").val()) {
MaxTurnsToNextFoodSpawn = parseInt($("#food-spawn-chance").val())
}
const snakes = []
$(".snake-group").each(function() {
const url = "http://0.0.0.0:8080/"//$(".snake-url", $(this)).val()
console.log(url)
if (!url) {
return
}
snakes.push({
name: "M1",//$(".snake-name", $(this)).val(),
url
})
})
if (snakes.length === 0) {
$("#errors").text("No snakes available")
}
console.log(12)
fetch("http://localhost:3005/games", {
method: "POST",
body: JSON.stringify({
width,
height,
food,
MaxTurnsToNextFoodSpawn,
"snakes": snakes,
})
}).then(resp => resp.json())
.then(json => {
const id = json.ID
fetch(`http://localhost:3005/games/${id}/start`, {
method: "POST"
}).then(_ => {
$("#board").attr("src", `http://localhost:3009?engine=http://localhost:3005&game=${id}`)
}).catch(err => $("#errors").text(err))
})
.catch(err => $("#errors").text(err))
})
console.log("ready!")
})
window.onload = function(){
var button = document.getElementById('start-game-btn');
button.form.submit();
console.log("Done!!")
}
Everytime I refresh the page the log shows:
loading...
ready!
Done!!
What I think it should be logging is (or at least that's what I'm trying to achieve):
loading...
start-game
ready!
Done!!
submit() does not work because it is for submitting forms.
button.form.submit(); does not work because it assumes your button is part of a form.
As it looks like you are using jQuery, try just calling the jQuery click function without any parameters using a jQuery selector.
eg.
$("#start-game-btn").click();