Good afternoon everyone!
So I have a basic Web Application. There are three dropdown menus and each dropdown menu appears after the previous dropdown has been selected. Once the third dropdown is chosen, we display to the user the TransferGuide (display_guide.php).
display_guide.php is outputted via an AJAX request on transfer.php
When a user click on the table header it expands to display courses under the category... that looks like this:
Everything is fine and works perfect. Only problem is, when I go to my dropdown menus to change to a different combination of dropdown menus, my table no longer toggles. It changes to display the right information, but it refuses to toggle. And the weird thing is that this ONLY happens on odd instances. The table will toggle ONLY the 1st, 3rd, 5th time I change the combinations and not the 2nd, 4th and so forth.
Does anyone have any idea what could be the reason for this odd behavior?
Here is my code and thanks in advance! This is only the <script> portion of the entire HTML/PHP code that is responsible of displaying the display_guide.php on to the page. :)
transfer.php:
<!--
**************************
AJAX FOR DROP DOWN MENUS
**************************
-->
<script>
// 1. Create an XMLHTTPRequest Object (XHR)
// 2. onreadystatechange is a property of the XHR Object, where we can store
// our function to execute once we send our XHR object, this is an
// event handler.
// 3. Check if readyState (values 0 thru 4) and status (values 200 or 404)
// in which case response is ready, in responseText, so we can assign to
// inner part of HTML element with ID of uni or major or transfer_guide
// 4. Open request with specified properties, with ?cc= as GET
// 5. Send the request using the open() property
function loadUniDropDown(str) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById("uni").innerHTML = xhttp.responseText;
}
};
xhttp.open("GET", "uni.php?cc="+str, true);
xhttp.send();
}
function loadMajorDropDown(str2) {
var xhttp2 = new XMLHttpRequest();
xhttp2.onreadystatechange = function() {
if (xhttp2.readyState == 4 && xhttp2.status == 200) {
document.getElementById("major").innerHTML = xhttp2.responseText;
}
};
xhttp2.open("GET", "major.php?uni="+str2, true);
xhttp2.send();
}
function loadTransferGuide(str3) {
var xhttp3 = new XMLHttpRequest();
xhttp3.onreadystatechange = function() {
if (xhttp3.readyState == 4 && xhttp3.status == 200) {
document.getElementById("transfer_guide").innerHTML = xhttp3.responseText;
// HIDES THE ALL THE GENED COURSES AND NOT THE GENED CATEGORIES BY DEFAULT ON PAGE LOAD
$(document).ready(function(){
//$("thead").next().hide();
$(".genEdCategoryHeaders").nextUntil(".genEdCategoryHeaders").hide(); //hides everything so only the categories show
$(".CourseName").hide(); //hides all the course names
}
);
// ON HOVER OVER GENED COURSES DISPLAY COURSE NAME INSTEAD OF NUMBER
$(".hoverOnCourses").mouseover(
function(){
$(this).children(".CourseNumber").hide();
$(this).children(".CourseName").show();
$(this).children(".CourseName").css("background-color","gray"); //gives hover effect by changing the background color
$(this).children(".CourseName").children().css("color", "white");
}
);
$(".hoverOnCourses").mouseout(
function(){
$(this).children(".CourseName").hide();
$(this).children(".CourseNumber").show();
}
);
// $("body").on("mouseover", ".CourseNumber", function(){
// $(this).hide();
// $(this).next().show();
// $(this).next().css("background-color","gray");
// $(this).next().children().css("color", "white");
// }).on("mouseout", ".CourseName", function(){
// $(this).hide();
// $(this).prev().show();
// })
//--------------TOGGLE THE GENED COURSES----------------
$(document).on("click", "thead", function(){
//$(this).next(".genedcat").toggle();
$(this).nextUntil("thead").toggle();
}
);
}
}
xhttp3.open("GET", "display_guide.php?major="+str3, true);
xhttp3.send();
}
</script>
If you have posted some basic html regarding the structure it would have been easier but still from what you have posted i could figure out one reason
In your loadTransferGuide() function see all the stuff after document.getElementById("transfer_guide").innerHTML = xhttp3.responseText;
you are assigning all the event handlers for mouseover and click events inside it which may look okay since you would want to assign events to the content loaded dynamically but i think this is what is causing the trouble you see when the transfer guide html gets inserted and you assign events it will work the first time but if they get replaced/removed again the event handler too get destroyed so it will work alternate times but not always to fix that just remove the code to fix that use event delegation on them
$("body").on("mouseover",".hoverOnCourses",
function(){
$(this).children(".CourseNumber").hide();
$(this).children(".CourseName").show();
$(this).children(".CourseName").css("background-color","gray"); //gives hover effect by changing the background color
$(this).children(".CourseName").children().css("color", "white");
});
$("body").on("mouseout",".hoverOnCourses",
function(){
$(this).children(".CourseName").hide();
$(this).children(".CourseNumber").show();
}
);
and cut below code and paste it outside of that function in global scope
//--------------TOGGLE THE GENED COURSES----------------
$(document).on("click", "thead", function(){
//$(this).next(".genedcat").toggle();
$(this).nextUntil("thead").toggle();
}
);
Related
I would like an element of my html page to be taken from another html file. So, such a widget displayed on many pages at once.
I was able to write JS code so that the content of the element is taken from another file. Code below:
//* Accordion - replace */
var fn = (event) => {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/widgets/accordion.html', true);
xhr.onload = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText)
document.getElementById("accordion").innerHTML = xhr.responseText;
}
else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.send(null); }
document.addEventListener('DOMContentLoaded', fn, false);
document.addEventListener
The item (accordion) displays correctly, as in the code here:
https://codepen.io/jakub-czeszejko-sochacki/pen/rNWNwrN
But unfortunately it doesn't work properly as if JS code is not being read for this element. As a result, when you click on the accordion button, the accordion does not open.
Is it even possible for this to work with JS?
Problem solved.
It turned out that the JS accordion initiation took place before its html was loaded with the line:
document.getElementById ("accordion"). innerHTML = xhr.responseText;
It was enough to put the initialization code (the accordion opening code) in the function and call this function after the above line of code.
Bare in mind I am very new to JavaScript syntax, so please have patience.
I'm trying to use multiple onreadystatechange's as promises to catch what I return from express, but every time I do, both of them are getting called. Here is my code:
var ready = function(){
xhr.open('GET', 'getallbeertypes');
xhr.send(null);
xhr.onreadystatechange = function () {
var parent = document.getElementById('recipes');
var docfrag = document.createDocumentFragment();
if(xhr.status == 200){
var beerTypes = JSON.parse(xhr.responseText);
//loop through beerTypes to append a button to each list item
for (var beer = 0; beer < beerTypes.length; beer++){
//create necessary elements
var li = document.createElement('li');
var button = document.createElement('BUTTON');
//set text content to list item
li.textContent = beerTypes[beer].alias;
//append list item to button
button.appendChild(li);
// add onclick attribute
button.setAttribute("name", beerTypes[beer]._id);
button.setAttribute("onclick", "moreInfo(this.getAttribute('name'))");
//append button to document fragment
docfrag.appendChild(button);
}
//display on DOM element
parent.appendChild(docfrag);
}else{
// console.log(JSON.parse(xhr.responseText));
var header = document.createElement('h1');
header.textContent = "Something went wrong. Please try again later.";
docfrag.appendChild(header);
parent.appendChild(header);
}
}
}
ready();
So the above section of code works, and it displays each JSON object as a button in a list of items. When the button is clicked, the following function is supposed to run:
var moreInfo = function(_id){
xhr.open('GET', '/getuserrecipes/' + _id);
xhr.send();
xhr.onreadystatechange = function(){
console.log("is this running?");
console.log(JSON.parse(xhr.responseText));
}
}
I'm expecting the onclick attribute should only run the moreInfo function.
Whenever I click one of the buttons, the else statement in the ready function is somehow running and displays Something went wrong. Please try again later on the screen.
The only connection I can make is the onreadystatechange, but I don't really know if that's it.
How/why is the other function even being called and How can I stop it? All help is appreciated.
You should check for the state of the request. To do that, use readyState property. There are 5 states, integers from 0 to 4, and you want the last one, which is 4.
something like this:
if (xhr.readyState == 4 && xhr.status == 200){
...
}
Check this example.
I have a simple javascript html file that I am using to perform a function that is called from menu item that I added to the Internet Explorer default context menu (through windows registry). The menu click triggers the following javascript:
<script type="text/javascript">
var req = new ActiveXObject("Microsoft.XMLHTTP")
req.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.status == 200) {
var serverResponse = req.responseText;
alert(serverResponse); // Shows "15"
}
}
};
req.open("POST", "https://www.googleapis.com/urlshortener/v1/url?key=myAPIkey", true)
req.setRequestHeader("Content-Type","application/json")
req.send('{"longUrl": "https://bing.com"}')
</script>
If I run this by opening the html file in IE it works fine. I get the response text. However, if I load it through the Context Menu the "onreadystatechange" function is never called.
If I add "alert("test") to the end of my script the test alert pops up followed a fraction of a second later by the serverResponse alert. I assume it has something to do with the alert blocking or forcing the browser to do something but I can't figure out why it works when loaded as a page but not through the Context menu.
I should note that the rest of the script DOES get called, so I know the menu item is working.
EDIT:
The above code snippet runs and displays an alert dialog containing the text of the response from the server whenever it's executed as an html page. If executed through the context menu I never get the "alert(serverResponse)".
However the following version does work when called from the context menu:
<script type="text/javascript">
var req = new ActiveXObject("Microsoft.XMLHTTP")
req.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.status == 200) {
var serverResponse = req.responseText;
alert(serverResponse); // Shows "15"
}
}
};
req.open("POST", "https://www.googleapis.com/urlshortener/v1/url?key=myAPIkey", true)
req.setRequestHeader("Content-Type","application/json")
req.send('{"longUrl": "https://bing.com"}')
alert("test")
</script>
The only difference is the inclusion of the "alert("Test")" line at the end
I'm in a real bottleneck with backbone.
I'm new to it, so sorry if my questios are stupid, as i probably didn't get the point of the system's structure.
Basically, I'm creating ad application which lets you do some things for different "steps". Therefore, I've implemented some kind of pagination system. Each time a page sasisfies certain conditions, the next page link is shown, and the current page is cached.
Each page uses the same "page" object model/view, and the navigation is appended there each time. it's only registered one time anyway, and I undelegate/re-delegate events as the old page fades out and the new one fades in.
If I always use cached versions for previous pages, everything is okay. BUT, if I re-render a page that was already rendered, when I click "go next page", it skips ahead of how many times i re-rendered the page itself.
it's like the "go next page" button has been registered, say, 3 times, and was never removed from the events listener.
It's a very long application in terms of code, and i hope you can understand the basica idea, and give me some hints, without needing to have the full code here.
Thanks in advance, i hope somebody can help me out since i'm in a real bottleneck!
p.s. for some reason, I've noticed that the next/previous buttons respective html is not cached within the page. Weird.
---UPDATE----
I tried the stopListening suggestion, but it didn't work. Here is jmy troublesome button:
// Register the next button
App.Views.NavNext = Backbone.View.extend({
el: $('#nav-next'),
initialize: function() {
vent.on('showNext', function() {
this.$el.fadeIn();
}, this)
},
events: {
'click': 'checkConditions'
},
checkConditions: function() {
//TODO this is running 2 times too!
console.log('checking conditions');
if (current_step == 1)
this.go_next_step();
vent.trigger('checkConditions', current_step); // will trigger cart conditions too
},
go_next_step: function() {
if(typeof(mainView.stepView) != 'undefined')
{
mainView.stepView.unregisterNavigation();
}
mainView.$el.fadeOut('normal', function(){
$(this).html('');
current_step++;
mainView.renderStep(current_step);
}); //fadeout and empty, then refill
}
});
Basically, checkConditions runs 2 times as if the previousle rendered click is still registered. Here is where it's being registered, and then unregistered after the current step fades off (just a part of that view!):
render: function() {
var step = this;
//print the title for this step
this.$el.attr('id', 'step_' + current_step);
this.$el.html('<h3>'+this.model.get('description')+'</h3>');
// switch display based on the step number, will load all necessary data
// this.navPrev = new App.Views.NavPrev();
// this.navNext = new App.Views.NavNext();
this.$el.addClass('grid_7 omega');
// add cart (only if not already there)
if (!mainView.cart)
{
mainView.cart = new App.Models.Cart;
mainView.cartView = new App.Views.Cart({model: mainView.cart})
mainView.$el.before(mainView.cartView.render().$el)
}
switch (this.model.get('n'))
{
case 5: // Product list, fetch and display based on the provious options
// _.each(mainView.step_values, function(option){
// console.log(option)
// }, this);
var products = new App.Collections.Products;
products.fetch({data:mainView.step_values, type:'POST'}).complete(function() {
if (products.length == 0)
{
step.$el.append('<p>'+errorMsgs['noprod']+'</p>')
}
else {
step.contentView = new App.Views.Products({collection: products});
step.$el.append(step.contentView.render().$el);
}
step.appendNavigation();
});
break;
}
//console.log(this.el)
return this;
},
appendNavigation: function(back) {
if(current_step != 2)
this.$el.append(navPrev.$el.show());
else this.$el.append(navPrev.$el.hide());
this.$el.append(navNext.$el.hide());
if(back) navNext.$el.show();
navPrev.delegateEvents(); // re-assign all events
navNext.delegateEvents();
},
unregisterNavigation: function() {
navNext.stopListening(); // re-assign all events
}
And finally, here is the main view's renderStep, called after pressing "next" it will load a cached version if present, but for the trouble page, I'm not creating it
renderStep : function(i, previous) { // i will be the current step number
if(i == 1)
return this;
if(this.cached_step[i] && previous) // TODO do not render if going back
{ // we have this step already cached
this.stepView = this.cached_step[i];
console.log('ciao'+current_step)
this.stepView.appendNavigation(true);
if ( current_step == 3)
{
_.each(this.stepView.contentView.productViews, function(pview){
pview.delegateEvents(); //rebind all product clicks
})
}
this.$el.html(this.stepView.$el).fadeIn();
} else {
var step = new App.Models.Step({description: steps[i-1], n: i});
this.stepView = new App.Views.Step({model: step})
this.$el.html(this.stepView.render().$el).fadeIn(); // refill the content with a new step
mainView.cached_step[current_step] = mainView.stepView; // was in go_next_step, TODO check appendnavigation, then re-render go next step
}
return this;
}
Try using listenTo and stopListening when you are showing or removing a certain view from the screen.
Take a look at docs: http://backbonejs.org/#Events-listenTo
All events are binded on initialization of the view and when you are removing the view from the screen then unbind all events.
Read this for detailed analysis: http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
I'm not very expert in using javascript and jquery but I'm working with them for a client.
I have encountered a problem using two script: the first one makes a top panel sliding, the second is in a form. This one is used in order to hide or show a particular field basing on the drop down list choice.
I've found that if I disable the first script (the panel), the second script is working fine and vice versa. I tried usign JQuery noConflict() in the head of the page but nothing happened.
Here the code of the first script (sliding panel):
$(document).ready(function () {
// Lets make the top panel toggle based on the click of the show/hide link
$("#sub-panel").click(function () {
// Toggle the bar up
$("#top-panel").slideToggle();
// Settings
var el = $("#shText");
// Lets us know whats inside the element
var state = $("#shText").html();
// Change the state
state = (state == 'Nascondi' ? '<span id="shText">Entra</span>' : '<span id="shText">Nascondi</span>');
// Finally change whats insdide the element ID
el.replaceWith(state);
}); // end sub panel click function
}); // end on DOM
Here the JS code for the form (hide/show field):
$document.addEvent('domready', function () {
$('motivo_contatto').addEvent('change', function () {
if ($('motivo_contatto').value == 'Invia CV') {
$('upload_file').style.visibility = 'visible';
} else {
$('upload_file').style.visibility = 'hidden';
}
});
$('upload_file').style.visibility = 'hidden';
});
});
Can anyone help me ? Thank you and have a nice day!
you're using 2 different ways to add things to happen to the document ready event:
$(document).ready(function(){ ... });
and
$document.addEvent('domready', function() { ... });
maybe if you just use one it works; maybe the code below will work; I put it all in the first option to run code on document ready:
I edited below code and removed all mootools code; so it might work now.
$(document).ready(function(){
// Lets make the top panel toggle based on the click of the show/hide link
$("#sub-panel").click(function(){
// Toggle the bar up
$("#top-panel").slideToggle();
// Settings
var el = $("#shText");
// Lets us know whats inside the element
var state = $("#shText").html();
// Change the state
state = (state == 'Nascondi' ? '<span id="shText">Entra</span>' : '<span id="shText">Nascondi</span>');
// Finally change whats insdide the element ID
el.replaceWith(state);
}); // end sub panel click function
document.getElementById('motivo_contatto').onchange = function() {
if(document.getElementById('motivo_contatto').value == 'Invia CV') {
document.getElementById('upload_file').style.visibility = 'visible';
} else {
document.getElementById('upload_file').style.visibility = 'hidden';
}
};
document.getElementById('upload_file').style.visibility = 'hidden';
}); // end on DOM
Mixing up two different libraries. Not a good idea.
If you want to keep on following on that pattern, wrap one of the function on a different function and call if from another.
Like:
function moo() {
$('motivo_contatto').addEvent('change', function () {
if ($('motivo_contatto').value == 'Invia CV') {
$('upload_file').style.visibility = 'visible';
} else {
$('upload_file').style.visibility = 'hidden';
}
});
$('upload_file').style.visibility = 'hidden';
});
}
Then call it from another
$(document).ready(function () {
moo(); // Call the moo function
// Lets make the top panel toggle based on the click of the show/hide link
$("#sub-panel").click(function () {
// Toggle the bar up
$("#top-panel").slideToggle();
// Settings
var el = $("#shText");
// Lets us know whats inside the element
var state = $("#shText").html();
// Change the state
state = (state == 'Nascondi' ? '<span id="shText">Entra</span>' : '<span id="shText">Nascondi</span>');
// Finally change whats insdide the element ID
el.replaceWith(state);
}); // end sub panel click function
}); // end on DOM
Check this answer, if you want to use both libraries side by side