Bind click events on loaded ajax contents - javascript

I'm trying to load contents through ajax and then perform some click events on those contents.
So basically I want to first load the contents(Buttons in this case) and then apply some click events on them .
and hence I'm using callbacks for the purpose to load the contents first and then apply click events , but here I've a problem that the content is loaded after the callback function, and I don't want this, I want to load the content first then execute callback function, how to solve this problem?
To achieve this till now I've the following code.
I've following code in food.php:
<button onclick="findWeek(fun)">week</button> // fun is callback function passed to findWeek()
// findWeek function ------ AJAX request to load buttons ------:
function findWeek(fun)
{
var xhr = new XMLHttpRequest();
xhr.open("GET" ,"start.php",true);
xhr.send(null);
xhr.onreadystatechange = function(){
if( (xhr.readyState == '4') && ( xhr.status == '200') ){
document.getElementById("stats").innerHTML = xhr.responseText;
}
};
fun();
}
// fun function, is a callback function, and I'm assuming that this will come in action when the findWeek() function load the contents,meaning that it will load the required buttons from the page start.php inserting those buttons to the following div which is inside food.php.
<div id = 'stats'>
</div>
And after that I want to be able to click those loaded buttons then which are supposed to be there in the above div.
That's why I've fun() function something like following.
Note: I've defined class .dayBtns for the loaded buttons.
function fun(){
function myfunction(){
alert('just clicked the button');
}
var dayBtns = document.getElementsByClassName('dayBtns');
alert('contents should be loaded');
for(var i = 0; i < dayBtns.length; i++){
console.log(dayBtns[i]);
btns[i].addEventListener('click', myfunction);
}
}
The problem is the content is loading after the fun() function execution, how to restrict fun() function not to execute until the data is not loaded ?
Please help , thanks !

Just place fun() into onreadystatechange like so -
function findWeek(fun) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "start.php", true);
xhr.send(null);
xhr.onreadystatechange = function() {
if ((xhr.readyState == '4') && (xhr.status == '200')) {
document.getElementById("stats").innerHTML = xhr.responseText;
fun();
}
};
}
The innerHTML is set after the function fun is called so there are no elements in the DOM at that point.
You also have a typo on the code here:
for(var i = 0; i < dayBtns.length; i++){
console.log(dayBtns[i]);
btns[i].addEventListener('click', myfunction);
}
Should be:
for(var i = 0; i < dayBtns.length; i++){
console.log(dayBtns[i]);
dayBtns[i].addEventListener('click', myfunction);
}
Notice dayBtns vs btns

Related

Appending a button on a div after div loads in the dom

I am trying to access an div (or here a tr tag) on which i want to append a button . But i am unable to access the tr tag because its loading after sometime and is not present in the DOM at that moment and getting error .
how to access a tag after loading something on the DOM
<script>
var btn = document.getElementById('btnn');
var tab = document.getElementsByClassName("scope")[0];
tab.append(btn)
</script>
I think your document.getElementById code should only be executed after everyting has been loaded. You could add an "load" Eventlistener and put your code inside it.
window.addEventListener("load", function(){
var btn = document.getElementById('btnn');
var tab = document.getElementsByClassName("scope")[0];
tab.append(btn)
});
By the way: I always use "defer" for my includes, like this:
<script src="{{ asset('/general/js/local.js') }}" defer></script>
This makes sure the "load" event will only be triggered after all includes have been loaded.
You could watch the DOM using the MutationObserver API. If the element you're observing is added, you could then apply your other code (e.g., append a button).
Basic Example:
let watchDOM = (function(){
let mo = window.MutationObserver;
return function(obj, callback){
if (!obj || !obj.nodeType === 1) {
return;
}
if (mo) {
let obs = new mo(function(mutations, observer) {
callback(mutations);
});
obs.observe(obj, { childList:true, subtree:true });
}
else if (window.addEventListener){
obj.addEventListener('DOMNodeInserted', callback, false);
obj.addEventListener('DOMNodeRemoved', callback, false);
}
}
})();
watchDOM(document.body, function(e) {
// This will notify you if a new DIV is added.
if (e[0].addedNodes[0].tagName === "DIV") {
// If the DIV is added, you can then take some action here.
// For example, you could append your button here.
console.log("div added");
}
});
// This adds a new DIV after 3 seconds of running the script
setTimeout(function() {
let newDiv = document.createElement("div");
document.body.appendChild(newDiv);
}, 3000);
Is this DIV getting created in the response of an AJAX call. If that is the case then you need to call you your button appending logic once the response of AJAX call has been received.
OR use this:
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
// - Code to execute when all DOM content is loaded.
// - including fonts, images, etc.
});
</script>

Don't show page until content has fully loaded

I am creating a landing page which should exist in two languages. The texts that should be shown are in two JSON files, called accordingly "ru.json" and "en.json". When a user clicks on the "Change language" button, the following function is executed:
function changeLang(){
if (userLang == 'ru') {
userLang = 'en';
document.cookie = 'language=en';
}
else {
userLang = 'ru';
document.cookie = 'language=ru';
}
var translate = new Translate();
var attributeName = 'data-tag';
translate.init(attributeName, userLang);
translate.process();
}
Where Translate() is the following:
function Translate() {
//initialization
this.init = function(attribute, lng){
this.attribute = attribute;
if (lng !== 'en' && lng !== 'ru') {
this.lng = 'en'
}
else {
this.lng = lng;
}
};
//translate
this.process = function(){
_self = this;
var xrhFile = new XMLHttpRequest();
//load content data
xrhFile.open("GET", "./resources/js/"+this.lng+".json", false);
xrhFile.onreadystatechange = function ()
{
if(xrhFile.readyState === 4)
{
if(xrhFile.status === 200 || xrhFile.status == 0)
{
var LngObject = JSON.parse(xrhFile.responseText);
var allDom = document.getElementsByTagName("*");
for(var i =0; i < allDom.length; i++){
var elem = allDom[i];
var key = elem.getAttribute(_self.attribute);
if(key != null) {
elem.innerHTML = LngObject[key] ;
}
}
}
}
};
xrhFile.send();
}
Everything works fine, however, when a user opens the page for the first time, if his Internet connection is bad, he just sees the elements of the page without text. It is just 1-2 seconds, but still annoying.
The question is, is there any way to check the text has loaded and display the page elements only on this condition?
You can use $(document).ready() in this way
$(document).ready(function(){
//your code here;
})
You can use the JavaScript pure load event in this way
window.addEventListener('load', function () {
//your code right here;
}, false);
Source: Here
translate.process() is asynchronous code which needs to make a call to a server and wait for its response. What it means is that, when you call this function, it goes in the background to go do its own thing while the rest of the page continues loading. That is why the user sees the page while this function is still running.
One minimal way I can think around this is by adding this to your css files in the head tag.
body { display: none }
And then, under this.process function, after the for loop ends, add
document.body.style.display = 'block'
If you want to suppori IE8:
document.onreadystatechange = function () {
if (document.readyState == "interactive") {
// run some code.
}
}
Put the code you want to execute when the user initially loads the page in a DOMContentLoaded event handler like below:
document.addEventListener('DOMContentLoaded', function() {
console.log('Whereas code execution in here will be deffered until the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.');
});
console.log('This will log immediatley');
It's important to note that DOMContentLoaded is different than the load event

Execute function in requested page when it is loaded

I have a problem with javascript in an ajax call
my script looks like this:
<html>
<body>
<div>contains large files to load</div>
<script type="text/javascript">
window.onload = function() {
alert('content is completely loaded');
}
</script>
</body>
</html>
When I reload the page it is working, but when I am dynamically loading this file with an ajax call.
my code to actually load and execute all scripts from the ajax call:
var tmp;
tmp = document.implementation.createHTMLDocument();
tmp.body.innerHTML = xmlhttp.responseText;
var scripts = tmp.getElementsByTagName('script');
var scripts_length = scripts.length;
for(i = 0; i < scripts_length; ++i) {
var script_tag = document.createElement('script');
script_tag.setAttribute('src', scripts[i].getAttribute('src'));
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('charset', 'utf-8');
document.body.appendChild(script_tag);
}
how can I let all scripts execute when someone refreshes the page and also when the content is loaded with ajax?
so: my scripts are loading but not executing because window.onload, but window.onload can't be removed because then the page refresh does not work anymore
You're lacking an eval function in your code, cause your described method only adds the scripts from the response to the page. You can try with the following function
// this function create an Array that contains the JS code of every <script> tag in parameter
// then apply the eval() to execute the code in every script collected
function parseScript(strcode) {
var scripts = new Array(); // Array which will store the script's code
// Strip out tags
while(strcode.indexOf("<script") > -1 || strcode.indexOf("</script") > -1) {
var s = strcode.indexOf("<script");
var s_e = strcode.indexOf(">", s);
var e = strcode.indexOf("</script", s);
var e_e = strcode.indexOf(">", e);
// Add to scripts array
scripts.push(strcode.substring(s_e+1, e));
// Strip from strcode
strcode = strcode.substring(0, s) + strcode.substring(e_e+1);
}
// Loop through every script collected and eval it
for(var i=0; i<scripts.length; i++) {
try {
eval(scripts[i]);
}
catch(ex) {
// do what you want here when a script fails
}
}
}
the complete article is available here http://coursesweb.net/ajax/execute-javascript-code-ajax-response_t
attach an onload event handler to the body tag like this or otherwise use an eventListner.and to check if when conetent is loaded by ajax.check for the onreadystatechange. for example
function makerequest(serverPage, objID) {
var obj = document.getElementById(objID);
obj.innerHTML = '<b>Loading....</b>';
xmlhttp.open("GET", serverPage);
xmlhttp.onreadystatechange = function() {
//perform mystunts
}
}
xmlhttp.send(null);
}
You can use the status code to check whatever you want to.
see Ref from w3school.com
The onreadystatechange event
When a request to a server is sent, we want to perform some actions based on the response.
The onreadystatechange event is triggered every time the readyState changes.
The readyState property holds the status of the XMLHttpRequest.
Three important properties of the XMLHttpRequest object:
onreadystatechange Stores a function (or the name of a function) to be called automatically each time the readyState property changes
readyState Holds the status of the XMLHttpRequest. Changes from 0 to 4:
0: request not initialized
1: server connection established
2: request received
3: processing request
4: request finished and response is ready
status 200: "OK"
404: Page not found

Javascript .each function acting abnormally

I have this function
function Overlap() {
setTimeout(function() {
$(".overlap").each(function(index, object) {
var image = $(this).prev().attr('src');
target = $(object);
var xmlhttp
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if(xmlhttp.status == 200 && xmlhttp.readyState == 4) {
xresponse = xmlhttp.responseText.split('-');
$(target).children('a').attr('href', xresponse[0]);
$(target).children('.name').html(xresponse[1]); //Set returned array appropriately
$(target).children('.age').html(xresponse[2]);
$(target).children('.specialization').html(xresponse[3]);
}
}
xmlhttp.open('POST', 'factory/operational.php', true);
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xmlhttp.send('overlap=overlap&image=' + image);
});
}, 1000);
}
Html:
<div class="Divbox" id="Box_three"><img src='<?php ?>' />
<div class="overlap">
<a href='' class='name'></a>
<div class='age'></div>
<div class='specialization'></div>
</div>
</div>
And it is called by this
window.onload = Overlap();
From the page that uses it
The problem is the function works .. it is usually called properly and i put the
window.onload & setTimeout functionality to make sure that the page loads fully before trying to grab the .overlap element.
Now what is wrong is when overlap is called there are about 48 of the .overlap div element on the page and they all send this request to the operational.php page but, on arrival back they do not appropriate properly, they only appropriate on the last element that comes back from operational.php, can someone help me find the right selector instance to pass to make sure that each instance of the overlap and is children receives the response accordlingly
you missed declaring your target variable, making it global.
target = $(object);
should be
var target = $(object);
without var, every iteration of the loop will use the same target, meaning all xhr request complete callbacks will use the same target.
Example:
$.each(["a","b","c"], function(i,val) {
x = val;
console.log(x); // "a", "b", "c"
});
console.log(x); //"c"
After the each is complete, x will be a global var that contains the value from the last iteration. In your code, you're performing an asynchronous action on each iteration, meaning, the callbacks for each action will happen after the each is complete, which is why target points to the last iteration for all of the callbacks.

Unobtrusive javascript not working with AJAX loaded content

I am facing very strange thing with AJAX and Unobtrusive JavaScript that I have two pages
ajaxcontent.php
index.php
index.php has
<div id="cont"></div>
<input type="button" id="a" value="load plz.">
<script type="text/javascript">
document.getElementById('a').onclick = function () {
if (window.XMLHttpRequest) {
ari = new XMLHttpRequest();
} else {
ari = new ActiveXObject("Microsoft.XMLHTTP")
}
ari.onreadystatechange = function () {
if (ari.readyState == 4 && ari.status == 200) {
document.getElementById('cont').innerHTML = ari.responseText;
}
}
ari.open("GET","button.php",true);
ari.send();
}
document.getElementById('b').onclick = function () {
alert('a');
}
</script>
And ajaxcontent.php has only
<input type="button" id="b"/>
and the problem is unobtrusive Javascript is not working.
After laoding of ajaxcontent when i click on button it doesn't show alert pop up.
i have tried that i added
document.getElementById('b').onclick = function () {
alert('a');
}
this code on ajaxcontent.php but it still not working.
THe only way to make it work that i have to add inline javascript as
<input type="button" id="b" onclick="hi();"/>
and replace this function with
document.getElementById('b').onclick = function () {
alert('a');
}
with
function hi() {
alert('a');
}
so please help me that how to use unobtrusive js here and please don't give jQuery based answer thanks
First of all document.getElementById('b') can only find an element that is in the DOM at the time you call this function.
Because the element with the id b is in the data you request in the click event, the function will not find any element. You most likely should have seen an error in the console like cannot set property onclick of undefined.
AJAX requests are async by default (and you should not make them sync because this will block the window of the browser).
So you need to place the document.getElementById('b').onclick = ... in the onreadystatechange check right after the document.getElementById('cont').innerHTML = ari.responseText;
Here a simple example how to generalize your request:
function doAjaxRequest(url, complete, error) {
var ari; //<<<< you should define your variables using var otherwise it is set in the global scope
if (window.XMLHttpRequest) {
ari = new XMLHttpRequest();
} else {
ari = new ActiveXObject('Microsoft.XMLHTTP');
}
ari.onreadystatechange = function() {
if (ari.readyState === 4) {
if (ari.status === 200) {
// if complete callback is passed, then call it if request was successful
if (typeof complete === 'function') {
complete(ari.responseText);
}
} else {
// if error callback is passed then call it if request was not successful
if (typeof error === 'function') {
error(ari.status, ari.statusText);
}
}
}
}
ari.open('GET', url, true);
ari.send(null);
}
document.getElementById('a').onclick = function() {
doAjaxRequest('button.php', function( data ) {
document.getElementById('cont').innerHTML = data;
document.getElementById('b').onclick = function() {
alert('a');
}
}, function(errorCode, errorMessage) {
//do something on error
});
}
The onclick event attaches to elements currently in the DOM when the function is triggered. Because the button in ajaxcontent.php is added to the DOM after the function was called, no event is attached.
To rectify this, you can add a snippet inside ari.onreadystatechange to detatch events then attach the event again.
ari.onreadystatechange = function () {
if (ari.readyState == 4 && ari.status == 200) {
document.getElementById('cont').innerHTML = ari.responseText;
// remove events
document.getElementById('b').onclick = null;
// attach events
document.getElementById('b').onclick = function() {
alert('a');
}
}
}
It's important to remove events because it may (although i was using jQuery when i learnt by mistake) cause double execution.
If you add javascript code inside ajaxcontent.php, that code will not be executed unless you extend your ari.onreadystatechange function to scan for javascript and execute it. The way I do this, is I put my javascript in AJAX requested pages in a input with class 'ajax-js' and scan for those input boxes and execute the code one by one, removing the class as I go.

Categories

Resources