Context: I'm lazy, and I'm trying to dynamically/automatically create menu buttons which are hyperlinked to the headers of a page with raw JavaScript.
My site loads the content of the body from an external file located at the same folder with document.onload, and I'm trying to set up a menu function, which then should load the default page's menu items. Loading the menu manually each time I change from one page to another works, as I've included loadMenues(thispage) on the end loadContents(), but it doesn't work as soon as the page is loaded, as loading the body content does. I don't understand this behaviour.
function setVisible(thisdiv){
var alldivs = document.getElementsByClassName("container");
[].forEach.call(alldivs, function(uniquediv){
document.getElementById(uniquediv.id).style.display = "none";
return;
});
document.getElementById(thisdiv).style.display = "block";
window.scrollTo(0,0);
loadMenues(thisdiv);
}
window.onload = function(){
loadContent("personalinfo");
loadContent("contactdetails");
setVisible("personalinfo");
loadMenues("personalinfo");
}
I'm explaining this, secondary question, in order to contextualize my main problem.
loadContents(file) is a function which extracts the contents from the requested file. The layout of each of these files is the same, pretty much, with each section of the file being separated by a custompadding div, where its first child is a h1 element as shown below:
<html>
<div class="custompadding">
<h1 id="headerpersonaldetails">Personal details</h1>
<p>Lorem ipsum</p>
</div>
<div class="custompadding">
<h1 id="headercontactdetails">Contact details</h1>
<p>Lorem ipsum</p>
</div>
</html>
I'm trying to set up a menu item for each of these headings, which scrolls to the clicked-on header. Setting up each menu-item manually works as expected, but I want to automatize it, so changing any file will automatically add the menu items to whichever page we change to. Following is my code which adds these elements to the divisor, but I'm having issues handling the onclick function.
function loadMenues(file) {
var rightmenu = document.getElementById("right-menu");
while(rightmenu.firstChild){
rightmenu.removeChild(rightmenu.firstChild);
}
[].forEach.call(document.getElementById(file).children, function(custompaddingchild) {
console.log(custompaddingchild);
headerelement = custompaddingchild.getElementsByTagName("h1")[0]
newbutton = document.createElement("div");
newbutton.setAttribute("class", "menu-item");
let movehere = function() { location.href="#"+headerelement.id; console.log(headerelement.id); }
newbutton.onclick = movehere;
/*rightmenu = document.getElementById("right-menu");*/
buttonspanner = document.createElement("span");
buttoncontent = document.createTextNode(headerelement.innerHTML);
buttonspanner.appendChild(buttoncontent);
newbutton.appendChild(buttonspanner);
rightmenu.appendChild(newbutton);
});
}
The first part of the function deletes all the nodes which already are in the menu, in order to add the new ones when changing pages.
Trying to define newbutton.setAttribute() with onclick results in a SyntaxError (fields are not currently supported) in Firefox. It doesn't work if I set a static string as newbutton.setAttribute("onclick", "location.href=#headerpersonalinfo"); either.
Trying to set a static anchor link with newbutton.onclick set to a function, instead, works, such that
newbutton.onclick = function() {
location.href = "#headerpersonalinfo";
}
and this is pretty much how my current code is set up, except that I have given this function a unique variable, which I then call.
The problem I have is this, as I see it: The variable is redefined each time it finds a new header, so calling the function sends the user to the last header, and not the expected one. How can I set the function to be parsed at the moment I define onclick with it, and not call the function when the user presses the button?
PS: I'm using my own internal naming convention of files, headers, and items, in order to modularize my site as much as I can. Since this is a website only intended for my Curriculum Vitae, I'm its only developer.
The issue occurs because the you are hoisting "variables" to the global scope (newbutton and headerelement).
Set them to block scoped variables (const or let) and you will see that it works:
https://codesandbox.io/s/rm4ko35vnm
function loadMenues(file) {
var rightmenu = document.getElementById("right-menu");
while (rightmenu.firstChild) {
rightmenu.removeChild(rightmenu.firstChild);
}
[].forEach.call(document.getElementById(file).children, function(
custompaddingchild
) {
console.log(custompaddingchild);
const headerelement = custompaddingchild.getElementsByTagName("h1")[0];
console.log(headerelement.innerHTML);
const newbutton = document.createElement("div");
newbutton.setAttribute("class", "menu-item");
console.log(headerelement.id);
let movehere = function() {
location.href = "#" + headerelement.id;
console.log(headerelement.id);
};
newbutton.addEventListener('click', movehere);
const rightmenu = document.getElementById("right-menu");
const buttonspanner = document.createElement("span");
buttoncontent = document.createTextNode(headerelement.innerHTML);
buttonspanner.appendChild(buttoncontent);
newbutton.appendChild(buttonspanner);
rightmenu.appendChild(newbutton);
});
}
Related
For a long time, I have been using a simple JavaScript file along with in-line, onclick events in 'a' tags to open a new window when the link is clicked. As you can see from the example below, I add the type and size of the window in the HTML. In the example below, the window cannot be resized by the user, is centered and is 1010 wide by 730 high.
Example HTML:
<a href="https://example.com" target='_blank' onclick="popUp(this.href,'elasticNoC',1010,730);return false;">
JavaScript file:
var newWin = null;
function popUp(strURL, strType, strWidth, strHeight) {
LeftPosition = (screen.width) ? (screen.width-strWidth)/2 : 0;
TopPosition = (screen.height) ? (screen.height-strHeight)/2 : 0;
if (newWin !== null && !newWin.closed)
newWin.close();
var strOptions="";
if (strType=="consoleC")
strOptions="resizable,top="+TopPosition+',left='+LeftPosition+",height="+
strHeight+",width="+strWidth;
if (strType=="fixedC")
strOptions="status,top="+TopPosition+',left='+LeftPosition+",height="+
strHeight+",width="+strWidth;
if (strType=="elasticC")
strOptions="toolbar,menubar,scrollbars,"+
"resizable,location,top="+TopPosition+',left='+LeftPosition+",height="+
strHeight+",width="+strWidth;
if (strType=="elasticNoC")
strOptions="scrollbars,"+
"resizable,top="+TopPosition+',left='+LeftPosition+",height="+
strHeight+",width="+strWidth;
if (strType=="console")
strOptions="resizable,height="+
strHeight+",width="+strWidth;
if (strType=="fixed")
strOptions="status,height="+
strHeight+",width="+strWidth;
if (strType=="elastic")
strOptions="toolbar,menubar,scrollbars,"+
"resizable,location,height="+
strHeight+",width="+strWidth;
if (strType=="elasticNo")
strOptions="scrollbars,"+
"resizable,height="+
strHeight+",width="+strWidth;
newWin = window.open(strURL, 'newWin', strOptions);
newWin.focus();
}
A recent update of a web application is now stripping in-line JavaScript so my old way of doing things no longer works.
I can still include separate JavaScript files but no in-line JavaScript.
I am thinking that the best option is to replace the in-line, onclick events with specific class names and use JavaScript to get the window type and size from the class name.
Example new HTML:
<a class="red elasticNoC-1010-730" href="https://example.com" target="_blank">
I can't figure out the correct JavaScript to use. Can someone please provide JavaScript code that can be used as a replacement? As you can see in the new HTML example, some of my links may contain more than one class name. In this example, the class name "red" would be ignored because it does not match any of the 'strType' in the JavaScript file.
Don't use classes, use data-* attribute:
<a class="red" data-popup="elasticNoC-1010-730" href="https://example.com" target="_blank">TEST CLICK</a>
and than the JS would be like:
// var newWin = null; function popUp( ............etc
const handlePopup = (ev) => {
ev.preventDefault(); // Prevent browser default action
const EL = ev.currentTarget; // Get the element
const args = EL.dataset.popup.split("-"); // Get the data-* parts
args.unshift(EL.getAttribute("href")); // Prepend HREF to parts
return popUp(...args); // Call popUp with arguments
};
const EL_popup = document.querySelectorAll('[data-popup]');
EL_popup.forEach(el => el.addEventListener('click', handlePopup));
Handling classes
Handling classes for custom attributes values is never a great idea, since it mostly becomes a parsing-things problem, because HTML attribute class can contain a multitude of classes in any order and number.
But luckily you could create a specific prefixed classname (with popUp-) like
popUp-elasticNoC-1010-730
and than use that specific prefix as your reference for splitting the specific string parts of interest, but also as your Elements selector:
querySelectorAll('[class*=" popUp-"], [class^="popUp-"]')
Here's an example:
const handlePopup = (ev) => {
ev.preventDefault(); // Prevent browser default action
const EL = ev.currentTarget; // Get the element
const classPopUp = [...EL.classList].filter(cl => cl.startsWith("popUp-"))[0];
const args = classPopUp.split('-'); // Convert class item to array
args.shift(); // remove "popUp-" prefix
args.unshift(EL.getAttribute("href")); // Prepend HREF to parts
return popUp(...args); // Call popUp with arguments
};
const EL_popup = document.querySelectorAll('[class*=" popUp-"], [class^="popUp-"]');
EL_popup.forEach(el => el.addEventListener('click', handlePopup));
<a class="red popUp-elasticNoC-1010-730 foo-bar" href="https://example.com" target="_blank">TEST CLICK</a>
Actually i am making list ... first i get the user info and store it into mysql database . when user clicks 'add' button a new element of list appears in the bottom of list as the part of list which is already coded in html. Now the problem is when i refresh the page the the new created element of list disappears ? Any suggestions?
<html>
<ul id="list" class="list-group" style="margin-top:20vh ;opacity:0.3;">
<span class="badge badge-primary badge-pill">+</span>Add shop
<a href="#" onclick="location.href='action.php';" class="list-group-item list-group-item-action" id="bg" >Emporium</a>
</ul>
</html>
<script>
function newuser() {
var ul = document.getElementById("list");
var li = document.createElement("a");
li.appendChild(document.createTextNode("<?php echo $row3['shopname'] ?>"));
li.setAttribute("id", "bg"); // added line
li.setAttribute("class", "list-group-item list-group-item-action");
li.setAttribute("href", "#");
// li.insertBefore(li, ul.children[0]);
ul.appendChild(li);
}
</script>
When you will reload the page, JS files will reload and execute again, so if you want some action based on some event and store that action event after reload, use cookie, local storage, or session storage and store some flag, based on that trigger your function on page load.
The page is rendered again every time you refresh the page. The JavaScript block will be executed again too. So nothing you create will 'inject' into your file with the code or stay there forever.
However, there are solutions that can help you achieve that (or simulate it). For example, localStorage or sessionStorage. Personally, I'd use localStorage for that purpose (unless there's some sensitive data included, e.g. bank account info and similar stuff). On page load where you'd have your form shown, you immediately invoke a method for getting an existing data object inside localStorage, e.g. localStorage.getItem(data). The localStorage and sessionStorage properties allow to save key/value pairs in a web browser. The localStorage object stores data with no expiration date. The data will not be deleted when the browser is closed, and will be available the next day, week, or year.
For example, if you want to keep the elements in a todo list, you might want to do something like this:
const addButton = document.querySelector("#addToDo");
const delBtn = document.querySelector("#delToDo");
addButton.addEventListener("click", addTask, false);
var tasksID = 0;
function saveTask(taskID, taskInfo) {
// Get tasks from local storage
let tasks = localStorage.getItem("todo-tasks");
if (!tasks) {
tasks = {};
} else {
tasks = JSON.parse(tasks);
tasks[taskID] = taskInfo;
}
// Save the result back
let infobj = JSON.stringify(tasks);
localStorage.setItem("todo-tasks", infobj);
}
function drawSavedTasks() {
let tasks = localStorage.getItem("todo-tasks");
if (tasks) {
tasks = JSON.parse(tasks);
Object.keys(tasks).forEach(k => {
addTask(null, tasks[k]);
});
}
}
drawSavedTasks();
function addTask(e, textToDo = undefined) {
if (!textToDo) {
textToDo = document.querySelector("#toDo").value;
}
var list = document.querySelector(".list");
var divToDo = document.createElement("div");
var p = document.createElement("p");
var delButton = document.createElement("button");
divToDo.setAttribute("id", "todo" + tasksID);
divToDo.setAttribute("class", "toDo");
delButton.classList.add("delToDo");
delButton.textContent = "Delete";
p.textContent = textToDo;
delButton.onclick = function() {
divToDo.parentNode.removeChild(divToDo);
}
divToDo.appendChild(p);
divToDo.appendChild(delButton);
list.appendChild(divToDo);
saveTask(tasksID, textToDo);
++tasksID;
}
.toDo {
width: 200px;
height: 80px;
border: 1px solid black;
margin: 5px;
padding: 5px;
text-align: center;
background-color: yellow;
}
<div class="form">
<input type="text" id="toDo" placeholder="To do...">
<button id="addToDo">Add</button>
</div>
<div class="list">
</div>
Note: The jsfiddle is not broken! Since JSFiddle loads the code under an iframe, you must select it on your developer console before running the code for the localStorage to work, or you might test it locally. If the HTML can not be seen, press the Full-page button. (Info here).
Note the functions saveTask and drawSavedTasks. drawSavedTasks is called first so anything you saved do be redrawn. Also, I don't save the entire previously created object, but only some relevant info about it, so I can re-create it.
Another solution would be if you'd use a database and/or a framework. Here, in the same way as with localStorage, you can save some metadata about the objects and redraw them on reload easily. For example, angularjs has a directive called ng-repeat where for every item in an array, a new element will be created when the page will be rendered. This can also be done with react or even laravel if you use php.
#foreach($itemas $itemsList)
<div>
Some data
</div>
#endforeach
Hope this helped you.
Cheers!
I'm appending some HTML to my button on a click, like this:
jQuery(document).ready(function($) {
$('#sprout-view-grant-access-button').on('click', function(e) {
$(this).toggleClass('request-help-cta-transition', 1000, 'easeOutSine');
var callback = $(e.currentTarget).attr('data-grant-access-callback');
var wrapper = $('.dynamic-container');
console.log(wrapper);
if( typeof window[callback] !== 'function') {
console.log('Callback not exist: %s', callback);
}
var already_exists = wrapper.find('.main-grant-access');
console.log(already_exists);
if( already_exists.length ) {
already_exists.remove();
}
var markup = $(window[callback](e.currentTarget));
wrapper.append(markup);
});
});
function generate_grant_access_container_markup() {
var contact_data_array = contact_data;
var template = jQuery('#template-sprout-grant-access-container')
return mustache(template.html(), {
test: 's'
});
}
As per the code, whatever comes from generate_grant_access_container_markup will be put inside dynamic-container and shown.
My problem is that, the newly added code just doesn't wanna dissapear upon clicking (toggle) of the button once again.
Here's my syntax / mustache template:
<script type="template/mustache" id="template-sprout-grant-access-container">
<p class="main-grant-access">{{{test}}}</p>
</script>
And here's the container:
<div class="button-nice request-help-cta" id="sprout-view-grant-access-button" data-grant-access-callback="generate_grant_access_container_markup">
Grant Devs Access
<div class="dynamic-container"></div>
</div>
I understand that the click event only knows about items that are in the DOM at the moment of the click, but how can I make it aware of everything that gets added after?
I would recommend visibility: hidden. Both display none and removing elements from the dom mess with the flow of the website. You can be sure you would not affect the design with visibility: hidden.
I don't deal with Jquery at all but it seems like this Stack overflow covers the method to set it up well.
Equivalent of jQuery .hide() to set visibility: hidden
I am stumped as to why my query .click() is not working. I am trying to change the href tag on an a element, before it goes to the next page.
here is my jquery
$('.individualFormSections').click(function() {
var formSectionTitle = $(this).siblings('div').text(); // gets section title that was clicked
console.log(formSectionTitle);
assetConfigIdForURL = assetConfigIdForURL.replace(/\s+/g,'-');
woTypeCodeForURL = woTypeCodeForURL.replace(/\s+/g,'-');
woMaintTypeCode = woMaintTypeCode.replace(/\s+/g,'-');
formSectionTitle = formSectionTitle.replace(/\s+/g,'-');
// Change href dynamically to set url parameters
$(this).attr("href",'airSystem.html?insp_asset_config_id='+assetConfigIdForURL+'&wo_type_code='+woTypeCodeForURL+'&wo_maint_type_code='+woMaintTypeCode+'&formSection='+formSectionTitle+'&wo_id='+woIdForURL+'');
});
Here is the html
<a class="individualFormSections" href="">
<img class="bus-form-img" src="pull-up.jpg" alt="Trolltunga Norway">
</a>
<div class="desc" id="bodyDamageDesc">AirSystem</div>
I also tried doing a simple alert and its not even targeting the a tag. My javascript link is set up correctly.
A little background, the html is getting generated dynamically from a previous javascript function. When I use chrome developer tools, all the html shows just fine. Any ideas on what could be causing this?
Always use prevent default in such cases
$('.individualFormSections').click(function(e) {
e.preventDefault();
var formSectionTitle = $(this).siblings('div').text(); // gets section title that was clicked
console.log(formSectionTitle);
assetConfigIdForURL = assetConfigIdForURL.replace(/\s+/g,'-');
woTypeCodeForURL = woTypeCodeForURL.replace(/\s+/g,'-');
woMaintTypeCode = woMaintTypeCode.replace(/\s+/g,'-');
formSectionTitle = formSectionTitle.replace(/\s+/g,'-');
// Change href dynamically to set url parameters
$(this).attr("href",'airSystem.html?insp_asset_config_id='+assetConfigIdForURL+'&wo_type_code='+woTypeCodeForURL+'&wo_maint_type_code='+woMaintTypeCode+'&formSection='+formSectionTitle+'&wo_id='+woIdForURL+'');
});
Change to this.
$('.individualFormSections').click(function(e) {
e.preventDefault();
var formSectionTitle = $(this).siblings('div').text(); // gets section title that was clicked
console.log(formSectionTitle);
assetConfigIdForURL = assetConfigIdForURL.replace(/\s+/g,'-');
woTypeCodeForURL = woTypeCodeForURL.replace(/\s+/g,'-');
woMaintTypeCode = woMaintTypeCode.replace(/\s+/g,'-');
formSectionTitle = formSectionTitle.replace(/\s+/g,'-');
// Change href dynamically to set url parameters
$(this).attr("href",'airSystem.html?insp_asset_config_id='+assetConfigIdForURL+'&wo_type_code='+woTypeCodeForURL+'&wo_maint_type_code='+woMaintTypeCode+'&formSection='+formSectionTitle+'&wo_id='+woIdForURL+'');
});
hey guys having trouble figuring out how to make it so that i can make it only open one table at once, once you open another the other should close any help here?
function showRow(cctab){
if (document.getElementById(cctab)) {
document.getElementById(cctab).style.display = '';
}
}
function hideRow(row1){
if (document.getElementById(cctab)) {
document.getElementById(cctab).style.display = 'none';
}
}
function toggleRow(cctab){
if (document.getElementById(cctab)) {
if (document.getElementById(cctab).style.display == 'none') {
showRow(cctab)
} else {
hideRow(cctab)
}
}
}
Now I want to make it so that only one table "cctab" opens after I suggest the onClick="javascript:toggleRow(cctab);" anyhelp?
Well you could save a reference to the previously shown item and hide it when another is shown:
var currentTab;
function showRow(cctab){
if (document.getElementById(cctab))
document.getElementById(cctab).style.display = '';
if (currentTab && currentTab != cctab)
hideRow(currentTab);
currentTab = cctab;
}
Note that doing inline event handler attributes is so 1999, but assuming you're sticking with it for whatever reason you don't need the javascript: in onClick="javascript:toggleRow(cctab);". (Just say onClick="toggleRow(cctab);")
First you need to store the old row somewhere.
What you've got is a system where you're using <element onclick="..."> to pass the id of the current element into the controller that shows or hides the row.
But if you look at that, what you're missing is a way of telling what the last open row was.
So what your code will need is a central object, or variables which store the old element and the new element.
How you do this is up to you, but if you did something like this:
var table_rows = { current : null /* or set a default */, previous : null };
function rowController (cctab) {
var newRow = document.getElementById(cctab);
if (newRow === table_rows.current) { toggleRow(newRow); }
else {
table_rows.previous = table_rows.current;
table_rows.current = newRow;
showRow(table_rows.current);
hideRow(table_rows.previous);
}
}
Note:
This deals with elements directly, so you don't have to do getById in your functions;
that's handled one time, and then that element is passed around and saved and checked against.
It assumes that the click is happening on the row itself, and not on anything inside of the row;
that's a separate issue that your code has.
Unless it's obvious and easy to click on the row, and not the cells inside of the row, it's difficult to tell how you want users to be able to open and close rows.
What I mean is if only the table-row has an onclick, and somebody clicks on a table-column, then then onclick isn't going to fire.