This's a simple video duration check program built with Electron. However, it doesn't work as expected; I also tried to use Electron IPC communication in others projects, but always fails.
I can upload a video successfully and submit it, after that nothings happens, any errors or advices. Debugger shows nothing too. I built a new project from zero and have same issue. Path value is also not showed on console
main.js:
const electron = require('electron'),
app = electron.app,
BrowserWindow = electron.BrowserWindow
const ffmpeg = require('fluent-ffmpeg')
const ipc = require('electron').ipcMain
let mainWindow
app.on('ready', () => {
mainWindow = new BrowserWindow({})
mainWindow.loadFile('./index.html')
})
ipc.on('video:submit', (event, path) => {
ffmpeg.ffprobe(path, (metadata) => {
event.returnValue = metadata.format.duration
})
})
index.html:
<html>
<head>
</head>
<body>
<form id="form">
<h1>Video Info</h1>
<div>
<label>Select a video</label>
<input type="file" accept="video/*" id="input">
</div>
<button type="submit" id="sb">Get info</button>
<div id="result"></div>
</form>
</body>
<script>
require('./renderer.js')
</script>
</html>
renderer.js:
const ipc = require('electron').ipcRenderer,
form = document.querySelector('#form')
let result = document.querySelector('#result')
console.log(path)
form.addEventListener('submit', () => {
const path = document.querySelector('#input').files[0].path
let reply = ipc.sendSync('video:submit', path)
result.innerHTML = 'Video is' + reply + 'seconds!'
})
EDIT
I made some changes on main and renderer to use asynchronous send and reply. I don't get what I want, but after submit some content, it's name is replaced with "No file chosen". Path value stills not printed.
changes on main.js:
ipc.on('video:submit', (event, path) => {
ffmpeg.ffprobe(path, (metadata) => {
let duration = metadata.format.duration
event.reply('duration', duration)
})
})
changes on renderer.js:
form.addEventListener('submit', () => {
const path = document.querySelector('#input').files[0].path
ipc.on('duration', (event, duration) => {
console.log(duration)
})
ipc.send('video:submit', path)
result.innerHTML = 'Video is' + duration + 'seconds!'
})
The code looks good but I guess the problem lies in your form submission. The page should be reloading on submit and it does not reach the IPC send
call. Try preventing default action for the form.
form.addEventListener('submit', (e) => {
e.preventDefault()
const path = document.querySelector('#input').files[0].path
ipc.on('duration', (event, duration) => {
console.log(duration)
})
ipc.send('video:submit', path)
result.innerHTML = 'Video is' + duration + 'seconds!'
})
Solved! There were 3 main errors so I don't know what exactly happened. But here are they:
1 - ipc functions were made wrong way. This is the right one (asynchronous reply):
renderer.js:
ipc.on('duration', (event, duration) => {
result.innerHTML = `Video is ${duration} seconds!`
})
ipc.send('video:submit', path)
main.js:
ipc.on('video:submit', (event, path) => {
ffmpeg.ffprobe(path, (err, metadata) => {
let duration = metadata.format.duration
console.log(duration)
event.reply('duration', duration)
})
})
2 - DOM wasn't getting submit button or event so "addEventListiner" was getting null... I changed submit button to input button with same id then cheanged submit event to simple click event. I query selected the input button rather the form at all. That's what I get finally:
renderer.js:
const ipc = require('electron').ipcRenderer,
suBtn = document.querySelector('input#sb')
let result = document.querySelector('div#result')
suBtn.addEventListener('click', (event) => {
event.preventDefault()
const path = document.querySelector('input#file').files[0].path
ipc.on('duration', (event, duration) => {
result.innerHTML = `Video is ${duration} seconds!`
})
ipc.send('video:submit', path)
})
3 - On ffprobe process on main.js, I was calling method with wrong params. It's necessary to pass the err param first, then data param, this way:
main.js:
ffmpeg.ffprobe(path, (err, metadata) => {
let duration = metadata.format.duration
console.log(duration)
event.reply('duration', duration)
})
Related
I hope someone can help me out with this one
My question is why does this code do exactly what I need?
var wfComponent;
fetch("https://nube-components.netlify.app/navbar01.json")
.then((res) => res.text())
.then((data) => (wfComponent = data))
.then(() => console.log(wfComponent));
document.addEventListener("copy", function (e) {
e.clipboardData.setData("application/json", wfComponent);
e.preventDefault();
});
document.getElementById("navbar01").onclick = function () {
document.execCommand("copy");
};
And this one does not do the copy to clipboard part?
$(".button.copy-button").on("click", function () {
let tag = $(this).attr("id");
console.log(tag);
var wfComponent;
fetch("https://nube-components.netlify.app/" + tag + ".json")
.then((res) => res.text())
.then((data) => (wfComponent = data))
.then(() => console.log(wfComponent));
document.addEventListener("copy", function (e) {
e.clipboardData.setData("application/json", wfComponent);
e.preventDefault();
});
document.getElementById(tag).onclick = function () {
document.execCommand("copy");
};
});
Now as you can see what I need is to "automate" that JSON location and target button part where I need each button to target a different URL. So I am now lost in this area where I manage to pull that id and apply it to the URL but the content does not get copied to the clipboard.
I am not a JS expert at all so please feel free to pinpoint anything I might be doing wrong or any ways to do this completely differently
Thanks
Because you use addEventListener inside the other. But either way, there is another (possibly hacky) way to achieve this.
$(".copy-button").on("click", function(e) {
let tag = $(this).attr("id");
fetch("https://nube-components.netlify.app/" + tag + ".json")
.then((res) => res.text())
.then((data) => (wfComponent = data))
.then(() => {
let copyFrom = document.createElement("textarea");
document.body.appendChild(copyFrom);
copyFrom.textContent = wfComponent;
copyFrom.select();
document.execCommand("copy");
copyFrom.remove();
console.log('COPIED!');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="navbar01" class="copy-button">copy navbar01</button>
<button id="navbar02" class="copy-button">copy navbar02</button>
I have a piece of javascript code that is switching between two different views of articles.
When I load the article index page I get the following error:
Uncaught ReferenceError: filterArticleToggle is not defined
I don't know what I have done wrong. I'm using the same code for profile pages and it works fine:
filterArticleToggle = function () {
const grid_articles_button = document.querySelector('#grid_articles')
const list_articles_button = document.querySelector('#list_articles')
const grid_articles_icon = document.querySelector('#grid_articles_icon')
const list_articles_icon = document.querySelector('#list_articles_icon')
const grid_articles_view = document.querySelector('#grid_articles_view')
const list_articles_view = document.querySelector('#list_articles_view')
if (grid_articles_button && list_articles_button) {
grid_articles_button.addEventListener('click', (e) => {
e.preventDefault()
grid_articles_icon.classList.add('text-brand-blue')
grid_articles_icon.classList.remove('text-inherit')
list_articles_icon.classList.remove('text-brand-blue')
list_articles_icon.classList.add('text-inherit')
fetch("/articles/switch_view/Grid").then(function(response) {
return "ok"
})
grid_articles_view.classList.value = "sm:-mx-6 lg:-mx-0 flex flex-wrap"
list_articles_view.classList.value = 'hidden'
})
list_articles_button.addEventListener('click', (e) => {
e.preventDefault()
grid_articles_icon.classList.remove('text-brand-blue')
grid_articles_icon.classList.add('text-inherit')
list_articles_icon.classList.add('text-brand-blue')
list_articles_icon.classList.remove('text-inherit')
fetch("/articles/switch_view/List").then(function(response) {
return "ok"
})
list_articles_view.classList.value = "sm:-mx-6 lg:-mx-0 flex flex-wrap"
grid_articles_view.classList.value = 'hidden'
})
}
}
document.addEventListener('turbolinks:load', () => {
filterArticleToggle()
})
window.filterArticleToggle = filterArticleToggle
This is in an article.js file that is being loaded in as the article index page loads.
Any help would be appreciated.
When I run my rails application and enter likeButton into the console it gives me Uncaught ReferenceError: likeButton is not defined
at :1:1
(anonymous) # VM1591:1
I tried moving the script in html to head and body. I am currently trying to use DOMContentLoaded but it seems I'm missing something. My overall goal is to change the color of the button once pressed and also keep the color after page refresh. I am using sessionStorage for this process. I just want to make sure that likeButton variable is declared after html is loaded. If its possible to done in javascript only.
//first js file
const BASE_URL = "http://localhost:3000"
const GPUS_URL = `${BASE_URL}/gpus`
const USERS_URL = `${BASE_URL}/users`
const gpuCollection = document.querySelector('#gpu-collection')
let wish = sessionStorage.getItem('wish');
class Gpu {
constructor(gpuAttributes) {
this.title = gpuAttributes.title;
this.price = gpuAttributes.price;
this.features = gpuAttributes.features;
this.link = gpuAttributes.link;
this.image = gpuAttributes.image;
this.id = gpuAttributes.id;
}
render() {
let div = document.createElement('div');
div.classList.add('card');
let h = document.createElement('h2');
let t = document.createTextNode(`${this.title} ($${this.price})`);
h.appendChild(t);
div.appendChild(h);
let h1 = document.createElement('h1');
h1.classList.add('gpu-cat');
h1.innerHTML = `${this.features}`;
div.appendChild(h1);
let button = document.createElement('button');
button.classList.add('list_btn');
button.innerHTML = '♡';
div.appendChild(button);
let a = document.createElement('a');
let img = document.createElement('img');
a.href = `${this.link}`;
a.target = '_blank';
img.src = `${this.image}`;
img.classList.add('gpu-image');
a.appendChild(img);
div.appendChild(a);
gpuCollection.appendChild(div);
}
}
//second js file
document.addEventListener("DOMContentLoaded", function (){
let likeButton;
SignUp();
logInUser();
logOutUser();
function putGpusOnDom(gpuArray){
gpuArray.forEach(gpu => {
let newGpu = new Gpu(gpu)
newGpu.render()
});
likeButton = document.querySelector("button");
}
function fetchGpus(){
fetch(GPUS_URL)
.then(res => res.json())
.then(gpus => putGpusOnDom(gpus))
}
const enableWish = () => {
console.log(likeButton)
sessionStorage.setItem('wish', 'red')
}
gpuCollection.addEventListener('click', function (){
wish = sessionStorage.getItem('wish');
if(wish !== 'red'){
enableWish();
}else{
disableWish();
}
});
})
//html file
...
<body>
<div id = "gpu-collection"></div>
<script type="text/javascript" src="src/Gpu.js"></script>
<script type="text/javascript" src="src/index.js" ></script>
</body>
</html>
As I mentioned in a comment the like button is not available on DOMContentLoaded if it is added dynamically. You need to wait until the button has been placed in the DOM
Use something like the following, I'm making some guesses here as there are some gaps in your code
document.addEventListener("DOMContentLoaded", function (){
//document.querySelector("button"); not yet available
//NOTE: The likeButton variable will ONLY be in scope INSIDE the event listener function
// You will not be able to access directly in the console.
let likeButton;
SignUp();
logInUser();
logOutUser();
function putGpusOnDom(gpuArray){
gpuArray.forEach(gpu => {
let newGpu = new Gpu(gpu)
newGpu.render()
});
//Now you have rendered the button it is available
//CAUTION: querySelector("button") will grab the first button on the page
// and ONLY the first button
likeButton = document.querySelector("button");
//Log like button to console while it is still in scope.
console.log(likeButton);
}
function fetchGpus(){
fetch(GPUS_URL)
.then(res => res.json())
.then(gpus => putGpusOnDom(gpus))
}
const enableWish = () => {
console.log(likeButton)
sessionStorage.setItem('wish', 'red')
}
})
using local storage and onclick with javascript
I have a html file with 2 job descriptions :
html file 1
<li><Job Reference Number: wru01</li>
<li><Job Reference Number: wru01</li>
I need to create a link (using javascript) that when each job description is clicked it auto fills out the form where the job description should be entered (this form is on another html page)
html file 2:
<legend>Job Application Information: </legend>
<label> Job Reference Number: </label>
<input id="refnumber" type="text" name="refnumber" required="required" />
so basically i need it that, when, and depending on which job number is clicked wru01 or wru02, it auto fills the job reference number in the form on the next page using local storage.
I have already tried this
js file 1
function onclick1() {
var anchor = document.getElementById('link');
anchor.addEventListener('click', function(event) {
event.preventDefault();
const jobCode = event.target.getAttribute('data-job');
localStorage.setItem('job-code', jobCode);
//need redirect user to apply page
//console.log(event.target)
window.location = event.target.getAttribute('href');
})
}
function onclick2() {
var anchor = document.getElementById('link2');
anchor.addEventListener('click', function(event) {
event.preventDefault();
const jobCode = event.target.getAttribute('data-job');
localStorage.setItem('job-code', jobCode);
//need redirect user to apply page
//console.log(event.target)
window.location = event.target.getAttribute('href');
})
}
function init() {
document.getElementById("link").onclick = function() {
onclick1()
};
document.getElementById("link2").onclick = function() {
onclick2()
}
window.onload = init;
}
js file 2
function LoadJobCode() {
var code = localStorage.getItem('job-code');
if (code) {
var input = document.getElementById('refnumber');
// disable text being entered
input.value = code;
input.disabled = true;
}
}
Excuse me,that's not a good idea to do it.I think you can use setTimeout to solve the problem.that's my code:
function onclick1() {
var anchor = document.getElementById('link');
anchor.addEventListener('click', function (event) {
event.preventDefault();
const jobCode = event.target.getAttribute('data-job');
console.log(jobCode)
localStorage.setItem('job-code', jobCode);
setTimeout(() => {
window.location.href = event.target.getAttribute('href');
},1000)
})
}
why did I do that?That's order to make sure to save the data(data-job) before entering another html page.Likewise,you can use async/await,such as below:
function onclick1() {
var anchor = document.getElementById('link');
anchor.addEventListener('click', function (event) {
event.preventDefault();
const jobCode = event.target.getAttribute('data-job');
console.log(jobCode)
localStorage.setItem('job-code', jobCode);
async function locate() {
await new Promise(() => {
window.location.href = event.target.getAttribute('href');
})
}
locate();
})
}
Okay, so on this project I have kind of a complicated set up. This is a Phaser 3 game. I load all of my scripts in the header of the html file. window.onload calls App.start(), which configures and loads Phaser and the scenes. In my title scene class, I make an ajax call and retrieve an html template which is then displayed in a modal. I cannot seem to handle events within the generated modal to work.
I've tried:
$('#loginForm').on('submit', (event) => {
event.preventDefault();
let values = $('#loginForm').serialize();
console.log(values);
}
and
$(document).on('submit', '#loginForm', (event) => {
event.preventDefault();
let values = $('#loginForm').serialize();
console.log(values);
}
as well as trying to bind to the actual submit button. With everything I've tried, the page reloads, and processes the form as a get submission (values are appended to the URL). I should note that neither the action not method of the form have been set; only the id.
What can I do to capture the submit event?
EDIT: Adding code
App.js
let App = function() {};
App.prototype.start = () => {
let scenes = [];
scenes.push(Loading);
scenes.push(Title);
scenes.push(Start);
let config = {
type: Phaser.AUTO,
width: 900,
height: 600,
parent: 'gameDiv',
scene: scenes
};
let game = new Phaser.Game(config);
...
};
Title.js (current testing)
class Title extends Phaser.Scene {
constructor(){
super({key: 'Title'});
}
preload(){}
create(){
let _t = this;
let btnLogin = this.add.sprite(300, 350, 'login');
let btnRegister = this.add.sprite(570, 350, 'register');
let logoText = this.add.bitmapText(80, 100, 'Lombardic', 'Dragon Court', 108);
btnLogin.setInteractive();
btnRegister.setInteractive();
btnLogin.on("pointerdown", (pointer) => {
DC.templateCall('user', 'mode=login_tpl', () => {
DC.templateFormHandler('loginForm', (event) => {
event.preventDefault();
let loginData = $("#loginForm").serialize();
console.log(loginData);
/*
modal.close();
Ajax.call('user', params, (result) => {
if(result.status){
_t.scene.start('Start', { fr: result.data.first_run, hc: result.data.has_char });
}else{
DC.modal('LoginError', '<div>'+result.error+'</div>', () => {});
}
});
*/
});
});
});
}
}
templateFormHandler function:
templateFormHandler: (id, callback) => {
$("#"+id).on("submit", callback);
}
I still can't find a legitimate reason for the code above not to work, but as a workaround, I've simply moved the handlers to the template file itself, and all works fine. A matter of scope somehow, I suppose.