Callback methods (a XMLHttpRequest) in a Javascript Class Object - javascript

I seem to get the following error: this.getPageHTML is not a function when executing my Modal class.
constructor(trigger) {
this.trigger = trigger;
}
/**
* Show Modal
* #param {Element} trigger The element that is triggering the Modal.
*/
showModal(trigger = this.trigger) {
switch (trigger.dataset.modaltype) {
case "media":
this.getPageHTML(trigger.dataset.url, (response) => {
const htmlContent = response.getElementById("content");
const modalContent = document
.getElementById("modal")
.querySelector(".modal-window__content");
// Inject response into Modal content
modalContent.insertAdjacentHTML("beforeend", htmlContent.innerHTML);
});
break;
default:
break;
}
}
getPageHTML(url, callback) {
if (!window.XMLHttpRequest) return;
const xhr = new XMLHttpRequest();
// Get the HTML
xhr.open("GET", url);
xhr.responseType = "document";
xhr.onloadstart = this.requestStarted;
xhr.onprogress = this.updateRequestProgress;
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
xhr.onload = function () {
if (callback && typeof callback === "function") {
callback(this.responseXML);
}
};
}
};
xhr.send();
}
Not sure why exactly my getPageHTML function is "not a function"?
In my main js file I call the class like this:
modalTriggers.forEach((trigger) => {
trigger.addEventListener("click", (e) => {
let modal = new Modal(trigger);
modal.showModal();
e.preventDefault();
});
});
Could be something super obvious I'm missing here. I have tried assigning the "this.getPageHTML" to an anonomous function like "this.getPageHTML = function(trigger.dataset.url, response) {...}" but it seems to loose what "trigger" is.

Related

GET request works in development, not in production

I have written the following script that should run a GET request to a specific URL when a set of checkboxes are ticked on and off. It works perfectly locally but in Production, I get a 404 response.
Any idea why and how to fix it?
/**
* Remove all child nodes from an element
* #param {Object} element The element to empty
*/
function empty (element) {
var children = Array.prototype.slice.call(element.childNodes);
children.forEach(function (child) {
element.removeChild(child);
});
}
function toggleItem(toggler, itemType, itemUrl, itemState, itemSource) {
let outputDiv = toggler.parentNode.querySelector('.theme-park-item-toggler-output');
empty(outputDiv);
let xhr = new XMLHttpRequest();
xhr.open('GET', itemUrl);
xhr.send(null);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { // 4 means "Done"
if (xhr.status === 204) { // 204 means processed successfully with no content returned
console.log('success');
if (itemState == true) {
let img = document.createElement('img');
img.src = itemSource;
outputDiv.appendChild(img);
}
} else {
console.log('Failure to toggle');
toggler.toggleAttribute('checked');
}
}
};
}
document.addEventListener("turbo:load", function() {
document.querySelectorAll('.theme-park-item-toggler').forEach(function(the_toggler) {
the_toggler.addEventListener('change', function(e) {
toggleItem(the_toggler, the_toggler.dataset.type, the_toggler.dataset.url, e.target.checked, the_toggler.dataset.source);
});
});
});

class method triggered without being called

I've defined a class to handle playing audio files. I'm instantiating the class, and calling its addEventListener() method at which time, playSound() is being triggered without the element being tapped. Also, when I call getEventListeners(bgMusic.elem) - the listener is no longer attached.
class WebAudio {
constructor(soundFile, elem) {
this.soundFile = soundFile;
this.elem = elem;
this.audio = new Audio('sound/' + this.soundFile);
}
addListener() {
this.elem.addEventListener('touchstart', this.playSound());
}
playSound() {
if (context.state != 'suspended') {
console.log('playing audio file');
if (!this.audio.playing) {
this.audio.play();
}
} else {
console.log("Audio Context locked? " + context.state)
}
}
}
var AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
function webAudioTouchUnlock(context) {
return new Promise( (resolve, reject) => {
//if AudioContext is suspended, and window has been interacted with
if (context.state === 'suspended' && 'ontouchstart' in window) {
console.log(context.state);
var unlock = () => {
//resume AudioContext (allow playing sound), remove event listeners
context.resume().then(() => {
console.log("context resumed");
document.body.removeEventListener('touchstart', unlock);
document.body.removeEventListener('touchend', unlock);
resolve(true);
}, function (reason) {
reject(reason);
});
};
document.body.addEventListener('touchstart', unlock, false);
document.body.addEventListener('touchend', unlock, false);
} else {
console.log('context not suspended? Context is ' + context.state);
resolve(false);
}
});
}
webAudioTouchUnlock(context);
let bgMusic = new WebAudio('bensound-clearday.mp3', document.querySelector('#sound_button'));
bgMusic.addListener();
When you add the event listener like:
this.elem.addEventListener('touchstart', this.playSound());
You care calling the function: this.playSound() and adding the result of that function (undefined) as the listener.
You just want to add the reference to the function:
this.elem.addEventListener('touchstart', this.playSound);
so the listener can call it when it needs too.
Also you will probably need to use something like this to maintain the proper this:
this.elem.addEventListener('touchstart', () => this.playSound());
or:
this.elem.addEventListener('touchstart', this.playSound.bind(this));

How to implement different behavior for function in anonymous function javascript

I am new to JavaScript and I want to use send_request function twice, but with different behaviour. Element with name button1 should show response on the element, whereas button2 not.
function send_request(url) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.send('data=test');
xhr.onload = function () {document.getElementById('reply').innerHTML = xhr.responseText;};
}
document.getElementById('button1').addEventListener('click', function() { send_request("/data.php"); });
document.getElementById('button2').addEventListener('click', function() { send_request("/clear_data.php"); });
Is it possible?
There are a number of ways to accomplish this, but if we just start with your basic requirement, you could have send_request simply take an argument that determines if the element should show the response.
function send_request(url, showResponse) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.send('data=test');
xhr.onload = function () {
// If showResponse is true, log the response. If not, don't
showResponse ? document.getElementById('reply').innerHTML = xhr.responseText : null;
};
}
document.getElementById('button1').addEventListener('click', function() {
// Call the function and indicate that the response should be shown
send_request("/data.php", true);
});
document.getElementById('bitton2').addEventListener('click', function() {
// Call the function and indicate that the response should not be shown
send_request("/clear_data.php", false);
});
You could give send_request another parameter, a function that's called with the responseText, so you could pass one function that assigns to reply, and another function that does whatever you want instead:
function send_request(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.send('data=test');
xhr.onload = () => callback(xhr.responseText);
}
document.getElementById('button1').addEventListener('click', function() {
send_request("/data.php", (responseText) => {
document.getElementById('reply').innerHTML = responseText;
});
});
document.getElementById('bitton2').addEventListener('click', function() {
send_request("/clear_data.php", (responseText) => {
console.log('bitton 2 response: ' + responseText);
});
});

click addEventListener is called without been clicked

I'm trying to add a function Fvote to all elements with class vote-up and vote-down.
var voteup = document.getElementsByClassName("vote-up");
var votedown = document.getElementsByClassName("vote-down");
function Fvote(upordown,postid) {
var x=this;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {console.log(xmlhttp.responseText);
if(xmlhttp.responseText=="error")
;
else{
/*some JS actions*/;}
}
xmlhttp.open("GET", "ajax/vote.php?q=" + postid + "q2="+upordown, true);
xmlhttp.send();
}
for(var i=0;i<voteup.length;i++)
voteup[i].addEventListener('click', Fvote("up",voteup[i].getAttribute("data-vote")), false);
for(var i=0;i<votedown.length;i++)
votedown[i].addEventListener('click', Fvote("down",votedown[i].getAttribute("data-vote")), false);
But when I load the page, it runs the function Fvote many times as the count of elements number, without clicking on any item. and if I clicked on items with class of vote-up or vote-down the function is not called. What I'm doing wrong?
You can get the parameters from within the function:
var voteup = document.getElementsByClassName("vote-up");
var votedown = document.getElementsByClassName("vote-down");
function Fvote(e) {
var x = e.target,
upordown = x.className.indexOf('vote-up') > -1 ? 'up' : 'down',
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
postid = x.getAttribute('data-vote');
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
console.log(xmlhttp.responseText);
if(xmlhttp.responseText=="error") {
/*some JS actions*/
} else {
/*some JS actions*/
}
}
xmlhttp.open("GET", "ajax/vote.php?q=" + postid + "q2="+upordown, true);
xmlhttp.send();
}
for(var i=0;i<voteup.length;i++)
voteup[i].addEventListener('click', Fvote, false);
for(var i=0;i<votedown.length;i++)
votedown[i].addEventListener('click', Fvote, false);
You got the wrong idea of how addEventListener works.
Basically it registers an event handler, which is an "address" of a function to execute when the event occurs. What you're doing is CALLING the function and EXECUTING it inside the loop.
Here's how it's normally used:
function handle() {
alert('An event!');
}
myElement.addEventHandler('click', handle);
Note that in this fragment, handle is passed without parenthesis thus "passing in the address", not invoking a call.

javascript function not responding after ajax request ( xmlhttprequest)

I have the following functions:
$(function() { //add new language
var lg_select = $('#add_language');
var table = lg_select.parent();
var table_head = $('form[name=languageData] tr').has('th')
$('.cv_addLang').click(function(e) {
e.preventDefault();
if(table_head.is(':hidden')) {
$('.nolangauge').hide();
table_head.show();
}
var new_lang = lg_select.clone();
new_lang.find('select[disabled=disabled]').removeAttr('disabled');
new_lang.find('select[name=new_language]').attr('name', 'language[]');
new_lang.find('select[name=new_level]').attr('name', 'language_level[]');
new_lang.appendTo(table).show();
})
})
function getXMLHttpRequestObject() { //ajax
var ajax = false;
if(window.XMLHttpRequest) {
ajax = new XMLHttpRequest();
} else if(window.ActiveXObject) {
try {
ajax = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
ajax = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
}
}
}
return ajax;
}
$(function() { //ajax
var ajax = getXMLHttpRequestObject();
if(ajax) {
$('div').delegate(".but_cv_w", "click", function(e){
e.preventDefault();
var div_content = $(this).parent().parent();
if(div_content) {
var x=$(div_content).attr('id');
//alert(x);
//alert(div_content);
var path = $(this).attr('href');
//alert(path);
ajax.open('get', path + '&ajax=true');
ajax.onreadystatechange=function() {
if(ajax.readyState == 4) {
if((ajax.status == 200) || (ajax.status == 304)) {
$(div_content).html(ajax.responseText);
} else {
$(this).click;
}
}
}
ajax.send(null);
return false;
}
})
}
})
The problem is that both new language and ajax are working fine but separated. If I delete the ajax function then new language function is working but if I keep the ajax function and make an ajax request then the other function (new language) isn't working anymore. It seams that after an ajax request the new language function dosen't work the seam as befor the ajax request.
The new language function is supposed to add new inputs for languages, the "cv_addLang" is the calss of an button which appears on the page after an normal server request or after a ajax request?
Hope someone could help me with this ??
Thanks for any help!
The Problem lies on the add new language function. Instead of an click event it is necesary to use delegate event and so the function should look like this in order for it to work.
$(function () { //add new language
$("body").delegate('.cv_addLang','click',function(e) {
e.preventDefault();
var lg_select = $('#add_language');
var table = lg_select.parent();
var table_head = $('form[name=languageData] tr').has('th');
if(table_head.is(':hidden')) {
$('.nolangauge').hide();
table_head.show();
}
var new_lang = lg_select.clone();
new_lang.find('select[disabled=disabled]').removeAttr('disabled');
new_lang.find('select[name=new_language]').attr('name', 'language[]');
new_lang.find('select[name=new_level]').attr('name', 'language_level[]');
new_lang.appendTo(table).show();
});
})

Categories

Resources