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.
Related
Here is my Javascript module:
const Calculator = (function() {
return {
listen: function (formId) {
this.formId = formId;
this.calculatorForm = document.querySelector(`#form_${this.formId}`);
if (this.calculatorForm) {
this.addEventListeners();
}
},
addEventListeners: function() {
const self = this;
this.calculatorForm.addEventListener('submit', function(event) {
console.log('calculatorForm submit', self);
self.calculatorSubmission(event);
}, false);
},
calculatorSubmission: function(event) {
event.preventDefault();
console.log('Form submitted', this.calculatorForm);
}
};
})();
export default Calculator;
I build all the Javascript using Webpack so I can load modules like this:
import Calculator from './modules/calculator';
The page in question where the Javascript is loaded has tabbed content. Each tab contains a different form, all using the Calculator module so when I switch between tabs, I call:
Calculator.listen('form-id');
The issue I have is when I switch between tabs a few times. Say I view tab 3, 5 times and then fill out and submit form in tab 3. The form is submitted 5 times because of the addEventListener called each time I view tab 3. Make sense?
I'm struggling to fix it - probably because I've been looking at it for hours now and my head is now mash.
Is the problem my module setup?
What best approach to overcoming my issue?
Thanks
I've updated my Calculator to be a class and this seems to work as expected now.
Any improvements welcome!
class Calculator {
constructor() {
if (!Calculator.instance) {
Calculator.instance = this;
}
this.calculatorSubmissionHandler = function(event) {
Calculator.instance.calculatorSubmission(event);
};
return Calculator.instance;
}
listen(formId) {
this.formId = formId;
this.calculatorForm = document.querySelector(`#${this.formId}`);
if (this.calculatorForm) {
this.addEventListeners();
}
}
addEventListeners() {
this.calculatorForm.addEventListener('submit', this.calculatorSubmissionHandler, false);
}
calculatorSubmission(event) {
event.preventDefault();
console.log('Form submitted', Calculator.instance.formId);
}
}
I am writing a pdf reading software using pdftron. I know that docViewer.on('mouseLeftDown', e => {} can get an event, but onClick can’t seem to get a mouse event. Is there any good solution? Thank you.
WebViewer(
{
path,
initialDoc: "",
},
viewer.value
).then(function (instance) {
const { Annotations, annotManager, docViewer } = instance;
instance.contextMenuPopup.add({
type: "actionButton",
label: "MD",
onClick: () => {
const freeText = new Annotations.FreeTextAnnotation();
freeText.PageNumber = docViewer.getCurrentPage();
freeText.X=?;// I don't know how to set the freeText.X at the location of the mouse
freeText.Y=?;
freeText.Width = 150;
freeText.Height = 50;
create-annotation-free-text
converting-between-mouse-locations-and-window-coordinates
I think one way you could do it is to listen to the contextmenu event on the iframe document and then store the last mouse event from there that you can then use in the onClick of your button.
For example
let lastContextMenuEvent;
instance.iframeWindow.document.addEventListener('contextmenu', (e) => {
lastContextMenuEvent = e;
});
instance.contextMenuPopup.add({
onClick: () => {
// use lastContextMenuEvent here
}
});
Task: To catch the form triggering on the page.
There is the following code:
$(document).ready(function () {
$(document).on('submit','form',function(e){
alert('wtf?');
})
})
In externally connected js there is a code of the following content, triggered by a click on a certain element:
h.redirect = function (a, b) {
var c = k.createElement("form");
c.action = b;
c.method = "post";
c.target = "_top";
c.style.display = "none";
var d = k.createElement("input");
d.type = "hidden";
d.name = "token";
d.value = a;
c.appendChild(d);
k.body.appendChild(c);
c.submit()
}
Attention question: With c.submit () nothing is caught, the coconut does not grow, the alert does not come out, but the form works and the submit occurs. How to catch an event, or at least explain why such crap? Doesn't c.submit () create a event?
UPD: Thank you all, I am a stupid elk, I did not look at the code properly, or where the callback is described without a submission.
In general, it is not very cool that a third-party script allows itself such freedoms such as directly invoking submit, or how specific it can be. This approach leaves no choice but to decorate:
(() => {
var old_submit = HTMLFormElement.prototype.submit;
HTMLFormElement.prototype.submit = function() {
var form = this,
args = Array.prototype.slice.call(arguments),
submit_event = new Event('submit', {
bubbles: true,
cancelable: true
});
submit_event.original_submit = function() {
old_submit.apply(form, args);
};
form.dispatchEvent(submit_event);
}
})();
$(() => {
var my_form = $( 'form' )[0];
$( document )
.on('submit', 'form', function(e) {
alert('wtf?');
e.originalEvent.original_submit();
})
.on('click', 'button', function() {
my_form.submit();
});
})
The point is to call out the object of the event when invoking the submission, in which there is a link to the original submit, which is the only way to send the form. This is horror, nightmare and hell, and I would never do that, but I didn’t think of anything better.
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();
})
}
I think I might have a problem with zombie views in my Backbone Marionette app.
How can I check for unclosed views and memory leaks? I'm using the illuminations-for-developers.com add-on for Firefox and as I move around my application I see over 1000 views piling up in the 'widgets' illuminations tab - and when I inspect the HTML for them the majority are not in the DOM. Are these zombied views?
Have added the code I'm using below to get peoples opinion on if I'm attacking the problem the right way.
I'm trying to build a UI similar to the Facebook multiple friend selector dialog (see pic).
I have a layout with two collection views, one populated with a list of users, and an empty one in which the selected users are added to.
I want to use this layout in multiple areas of my app. So I have built a controller object that handles initializing it and loading the data for the collections, and then I initialize it and show it in another region whenever it is required.
Would appreciate tips on how to go about this, thanks.
Codez:
MyApp.UserFilterController
MyApp.UserFilterController = (function(){
var UserFilterController = {};
var selectedUsersCol;
var userFilterColView;
var selectedUsersColView;
var usersCol;
UserFilterController.initialize = function ( callback, excludeUsers ) {
// make a query...
// exclude the users...
var usersQ = new Parse.Query(Parse.User);
// just users with email addresses
usersQ.exists('email');
usersQ.exists('name');
usersQ.limit(1000);
usersQ.ascending('name');
usersQ.notContainedIn('objectId',excludeUsers);
usersCol = usersQ.collection();
// tell it where to render... append to the body give it an element?
userFilterColView = new MyApp.UserFilterUserCollectionView({
collection:usersCol
});
usersCol.fetch({
success:function (col) {
console.log("users collection fetched", col.length);
},
error:function () {
console.log("error fetching users collection");
}
});
$('#subpage-header').text("Users Selection");
// empty collection to hold the selected users
selectedUsersCol = new MyApp.Users();
// view to show the selected users
selectedUsersColView = new MyApp.SelectedUserCollectionView({
collection:selectedUsersCol
});
_.extend(selectedUsersCol, newBackboneAddMethod());
MyApp.userFilterLayout = new MyApp.UserFilterLayout();
MyApp.slideUp.content.show(MyApp.userFilterLayout);
MyApp.userFilterLayout.selectedusers.show(selectedUsersColView);
MyApp.userFilterLayout.allusers.show(userFilterColView);
//When user clicks on user in all users then its added to selected users
userFilterColView.on("itemview:clicked", function(childView, model){
console.log(model);
selectedUsersCol.add(model);
});
userFilterColView.on("collection:rendered", function(childView, model){
console.log('its rendered');
});
//When user clicks on selected user then it is removed from the collection
selectedUsersColView.on("itemview:clicked", function(childView, model){
console.log(model);
console.log(model.id);
selectedUsersCol.remove(model);
});
MyApp.App.vent.bind("slideUp:send",function(){
console.log("send button has been clicked. attempting call back")
callback(selectedUsersCol);
});
//unbinds the trigger above when view is being closed
userFilterColView.on('collection:before:close', function (){
MyApp.App.vent.unbind("slideUp:send");
console.log('colView before:close')
});
};
UserFilterController.removeUser = function ( user ) {
//console.log("you asked to remove", usersArray.length, 'users');
selectedUsersCol.remove(user);
usersCol.remove(user);
};
UserFilterController.generateListview = function ( user ) {
userFilterColView.$el.listview();
};
UserFilterController.resetSelected = function (user) {
selectedUsersCol.reset();
};
UserFilterController.cleanup = function () {
console.log("its closing");
//selectedUsersColView.unbindAll();
// selectedUsersColView.close();
userFilterColView.close();
// userFilterLayout.unbindAll();
// MyApp.userFilterLayout.close();
// MyApp.slideUp.content.close();
// MyApp.slideUp.close();
};
return UserFilterController;
}());
MyApp.EventDisplayLayout
MyApp.EventDisplayLayout = Backbone.Marionette.Layout.extend({
template: '#event-display-layout',
id: "EventDisplayLayout",
events: {
'click #invite': 'showUserFilter'
},
// User clicked on 'invite' button
showUserFilter: function () {
$.mobile.changePage($('#subpage'), {changeHash: false,transition: 'slideup'});
MyApp.UserFilterController.generateListview();
}
}
MyApp.showEventDisplay
MyApp.showEventDisplay = function (event) {
var eventDisplayLayout = new MyApp.EventDisplayLayout({});
MyApp.App.mainRegion.show(eventDisplayLayout);
var Invitees = event.get("invitees");
var excludeIds = [];
_.each(Invitees,function(invitee){
excludeIds.push(invitee.id);
});
MyApp.UserFilterController.initialize(function (selectUsersCol){
console.log("In call back: ",selectUsersCol);
},excludeIds);
};
MyApp.SlideUpPageLayout
// The generic layout used for modal panel sliding up from bottom of page.
MyApp.SlideUpPageLayout = Backbone.Marionette.Layout.extend({
el: '#subpage',
//template: '#homepage-temp',
regions: {
header: '.header',
content: '.content'
},
events:{
'click .send':'slideUpSend',
'click .cancel':'slideUpCancel'
},
onShow: function () {
console.log("SlideUpPage onShow");
this.$el.trigger('create');
},
initialize: function () {
// make user collection...
},
slideUpSend: function () {
console.log("send button has been pressed");
MyApp.App.vent.trigger('slideUp:send');
$.mobile.changePage($('.type-home'),{transition: 'slideup',reverse:true});
},
slideUpCancel: function () {
// MyApp.App.vent.trigger('slideUp:cancel');
$.mobile.changePage($('.type-home'),{transition: 'slideup',reverse:true});
}
});
MyApp.UserFilterLayout
// The layout used for the user filter panel sliding up from bottom of page.
MyApp.UserFilterLayout = Backbone.Marionette.Layout.extend({
template: '#userfilterlayout',
//template: '#homepage-temp',
regions: {
selectedusers: '.selectedusers',
allusers: '.allusers'
},
onShow: function () {
console.log("userfilterlayout onShow");
this.$el.trigger('create');
}
});