dynamically add dropdown menu with ajax - javascript

I'm trying to implement a form with a dropdown menu that has element from a database and every time i pick an element from that menu another one appears. I'm trying to do this with ajax but i have no idea how to
i know that i have to use this function to start, but i don't know how to implement the rest of it
function ajaxRequest() {var request=false;
try { request = new XMLHttpRequest()}catch(e1){
try{request = new ActiveXObject("Msxml2.XMLHTTP")}catch(e2){
try{ request = new ActiveXObject("Microsoft.XMLHTTP")
}catch(e3){request = false} }
}
return request }

If you are getting json data of menu elements in array then you can iterate and create a menu like this:
let menuHtml = "<ul>";
menuItems.forEach(item => {
menuHtml += `<li>${item.text}</l1>`;
}
menuItem += "</ul>;
// Now your menu HTML is ready. It's time to inject menuHtml on DOM
document.querySelector("#whereToInjectMenuHtml").innerHTML = menuHtml;
If you are getting HTML from api then you can use
document.querySelector("#whereToInjectMenuHtml").innerHTML = yourApiHtml;

Related

onClick Add to Grid from an Array but duplicates on second iteration

Use case is to create Add/show/select/delete text messages on a page. I have used array to store the messages on each click till here it works fine but when I try to create Divs and show the messages in grid, I am having duplication on each click. I can see that my function is reading the array fine and creating the div's on the first click but it does not clear the previously created div on second iteration. Not sure how to approach this. Please advise.
here is the snippet.
<script>
//<!-- Fetch data from Textarea when user Click Save and create or push in an array -->
let ArrNotes = [];
function SaveNote() {
let edittext = document.getElementById("editor").value;
ArrNotes.push(edittext);
document.getElementById("SavedNote").innerHTML = ArrNotes;
let container = document.getElementById("GridNotes");
container.className= "GridNotes";
ArrNotes.forEach(createGrid);
function createGrid(item,index)
{
text = undefined;
var tag = document.createElement("div");
tag.className= "grid";
var text = document.createTextNode(index + ": " + item);
//tag.appendChild(text);
container.appendChild(text);
}
}

In Web Components / connectedCallback fn, I am importing a JSON file using XHR, and displaying it. But unable to use query selector. Why?

I am combining 2 concepts of XHR get (which fetches JSON URL from an attribute in web component) and displaying the JSON data as a list. All that is working fine.
But when I want to toggle the list on / off, somehow the query selector All doesnt work. but it does work in browser console.
Maybe because in connected callback, the DOM hasnt been rendered, hence it cannot be edited ? I have tried rendered Callback / render / set timeout but nothing works. Also tried 2 connectedCallback functions, but query selector still doesnt detect the JSON list.
In browser console, document.querySelectorAll('.history-list') works fine. What should I do to make it work. Many thanks.
Here is the code :
connectedCallback(){
let lock_time_ul = document.querySelector('#lock-time-ul');
// fetch data from JSON file (WORKING)
// fetch JSON URL value (ONLY if attribute has been set)
if (this.hasAttribute('json-url'))
{
this.json_url = this.getAttribute('json-url');
// fetch data from this URL
const xhr = new XMLHttpRequest();
xhr.open('GET', this.json_url);
xhr.onload = function(){
this.json_output = JSON.parse(xhr.response);
// (WORKING FINE)
this.json_output.forEach(el => {
// change el.type from enrollment -> locked and cancellation -> unlocked
if (el.type == "enrollment")
{
el.type = "Locked";
}
else if(el.type == "cancellation")
{
el.type = "Unlocked";
}
var li = document.createElement('li');
var span = document.createElement('span');
span.className = "date";
li.className = "history-list";
span.appendChild(document.createTextNode(el.date));
var div_lock_wrapper = document.createElement('div');
div_lock_wrapper.className = "lock-wrapper";
div_lock_wrapper.style.fontWeight = 500;
div_lock_wrapper.appendChild(document.createTextNode(el.type));
li.appendChild(span);
li.appendChild(div_lock_wrapper);
lock_time_ul.appendChild(li);
})
}
xhr.send();
}
// javascript for sidebar show / hide (NOT WORKING, QUERY SELECTOR ALL history-list SHOWS NULL)
let toggle_lock_history_btn = document.querySelector(".history-title");
let toggle_lock_history_list = document.querySelectorAll(".history-list");
let toggle_show_all_text = document.querySelector(".show-all");
toggle_lock_history_btn.addEventListener('click', function()
{
// check the history-title first
if (toggle_lock_history_btn.textContent == "Hide lock history")
{
// toggle off
toggle_lock_history_list.forEach(el => {
el.style.display = "none";
})
// hide show-all
toggle_show_all_text.style.display = "none";
// change Hide to Show in history-title
toggle_lock_history_btn.textContent = "Show lock history";
}
else if (toggle_lock_history_btn.textContent == "Show lock history")
{
// toggle on
toggle_lock_history_list.forEach(el => {
el.style.display = "";
})
// show show-all
toggle_show_all_text.style.display = "";
// change Show to Hide in history-title
toggle_lock_history_btn.textContent = "Hide lock history";
}
})
} // connectedCallback ENDS
My HTML
<test json-url="data.json"></test>
Got the solution :
window.onload = init;
function init(){
// the code to be called when the dom has loaded
}
since the element I was trying to get by ID was created dynamically (loaded by ajax and created by script).

Data binding in javascript

I am trying to implement a simple notification system for a personal project, but I got stuck in how to bind events to the notifications in order to response to user actions, like accept or deny.
So far what i got is an object which is created dynamically.
/* reference to notifications */
var notifications = [];
// the notification object
var Notification = function(travelInfo){
this.el = $('#notifications_container');
this.id = Math.random().toString().substring(7);
this.travelInfo = travelInfo;
this.read = false;
};
Notification.prototype.init = function(){
this.addOne();
this.render();
this.bindFunctions();
}
/* dynamically created */
for(var i = 0; i < 10; i++){
var notification = new Notification(travelInfo);
notifications.push(notification);
notification.init();
}
This object is supposed to render and actually bind events to the rendered element in order to keep track of the actions of every notification independently:
// render the view of a notification
Notification.prototype.render = function(){
var source = $('#notification-template').html();
var template = Handlebars.compile(source);
var data = this.travelInfo;
result = template(data);
$('#notifications_container').append(result);
}
Notification.prototype.addOne = function(){
var number = +$('#notifications_indicator').html();
$('#notifications_indicator').html(number += 1);
}
Notification.prototype.removeOne = function(){
var number = +$('#notifications_indicator').html();
$('#notifications_indicator').html(number -= 1);
}
Notification.prototype.alertId= function(){
alert('the notification with id:' + this.id + ' is accepted');
}
Notification.prototype.bindFunctions = function(){
var self = this;
this.el.on('click', 'li .accept', function(){
self.alertId();
});
}
The problem with my approach is that actually if I create 10 elements the click event is triggered 10 times showing 10 alerts. What I want to know is how can I implement a system in which I render an element and also bind events to that element something like backbone's way to bind events to every view item. I feel backbone is so big for what I need right now that is just the notification system. I hope you guys could help me to find a way to do this.
If I am reading your question correctly, you are over-thinking this one a little bit.
I believe that you are looking for something like this:
<div>
<!-- This is where your HTML is -->
<button type="button" data-post-id="10">Delete this post</button>
<button type="button" data-post-id="11">Delete this post</button>
</div>
Now, on your onclick handler:
function handleDeleteClick() {
// `this` should be a reference to the element.
postId = this.getAttribute("data-post-id");
// Do something with postId
}
To complete out the code...
element.setAttribute("data-post-id", "11");
element.removeAttribute("data-post-id");

Adding content to menu items

I've made some menus, but can't figure out how to set the actual page content for these menu items once clicked. Cheers.
function doGet() {
var app = UiApp.createApplication();
var menu = app.createMenuBar();
var handler = app.createServerHandler();
var menuUsers = menu.addItem('Users', handler).addSeparator().setId('users');
var menuPending = menu.addItem('Pending Submissions', handler).addSeparator().setId('pending');
app.add(menu);
return app;
}
In general, you would have some main panel that is your main display. Let's call it main-display".
The handler for each menu item executes a function when the menu item is clicked. This function can do anything (say, highlight some text, or pull up a toolbar, or save the file). It doesn't necessarily have to change the whole display. A menu bar is like the File menu or Edit or whatever you'd like.
You would have to define a ServerHandler that handles each different menu item being clicked.
function doGet() {
var app = UiApp.createApplication();
var menu = app.createMenuBar();
var handlerUsers = app.createServerHandler("showUsers");
var handlerPending = app.createServerHandler("showPending");
var menuUsers = menu.addItem('Users', handlerUsers).addSeparator().setId('users');
var menuPending = menu.addItem('Pending Submissions', handlerPending).addSeparator().setId('pending');
app.add(app.createVerticalPanel().add(menu).add(app.createSimplePanel().setId("main-display")));
return app;
}
Then have some functions
function showUsers() {
var app = UiApp.getActiveApplication();
var main = app.getElementById("main-display");
//do whatever you need to display your main panel
return app;
}
Similar function for showPending.
Instead, if you'd like a bunch of different content panels, look into using TabPanel. I feel like this is more of what you're looking for.

Add a js function to the DOM?

I have a very simple HTML page. After everything is loaded, the user can interact with it perfectly. Now, at some point, the user clicks on an element. An ajax call is made and new data is being requested. I now want to remove the previous element the user clicked on with the element(s) the user has requested (on the same page) - practically remove the old element from the DOM and add the new one. Well, I did this as well, but I am unable to add a function to the newly created element. This is my function:
setCountry = function(value){
document.getElementById('country').innerHTML = value;
}
and I'm trying to add it like this to my element
a_tag.setAttribute("href", "javascript:setCountry(countries[i]);");
The function is being called and writes "undefined" to the innerHTML element. I set the attribute using a for loop and just above the for loop I alert an element from the array to be sure it's correct, and it prints out the correct value.
I assume the problem happens because the function is being created on the first load of the DOM, but I'm not sure. Can anyone shed some light on what is really happening here and what I should do to correct it? I want to be able to add more functions so not looking for a work around writing an innerHTML tag, I just want to understand what I'm doing wrong.
Thank you.
Edited with more code
//declare an array to hold all countries form the db
var countries = new Array();
function getCountries(region) {
document.getElementById('scroller').innerHTML = '';
//send the data to the server and retreive a list of all the countries based on the current region
$.ajax({
type: "POST",
url: "scripts/get_countries.php",
data: {
region: region
},
success: saveDataToArray,
async: false,
dataType: 'json'
});
//save the data to an array
function saveDataToArray(data){
var i = 0;
while (data[i]){
countries[i] = data[i];
i++;
}
}
scroller = document.getElementById('scroller');
//create a ul element
var holder = document.createElement("ul");
//here create a back button which will recreate the whole list of regions
var total = countries.length;
for(i=0;i<total;i++){
//create the first field in the list
var bullet_item = document.createElement("li");
//create an a tag for the element
var a_tag = document.createElement("a");
//set the redirect of the tag
a_tag.setAttribute("href", "javascript:setCountry(this);");
//create some text for the a_tag
var bullet_text = document.createTextNode(countries[i]);
//apend the text to the correct element
a_tag.appendChild(bullet_text);
//apend the a_tag to the li element
bullet_item.appendChild(a_tag);
//apend the item to the list
holder.appendChild(bullet_item);
}
//apend the holder to the scroller
scroller.appendChild(holder);
}
function setRegion(region){
document.getElementById('region').innerHTML = region;
}
setCountry = function(value){
document.getElementById('country').innerHTML = value;
}
There is no need for quoting the code in a string. Instead of this:
a_tag.setAttribute("href", "javascript:...")
Try to form a closure:
a_tag.onclick = function () { ... }
Note that by default <A> elements without HREF do not look normal, but you can fix that with CSS.
Problem solved
Everything was good apart from the way I was declaring the href parameter
a_tag.setAttribute("href", "javascript:setCountry("+'"'+countries[i]+'"'+")");
it's all the usual, a game of single quotes and double quotes.
Thanks everyone for pitching in ideas. Much appreciated as usual
Adrian

Categories