So basically I was trying to make an error message that is hidden by default be displayed when the user inputs incorrect information. However, the message wasn't appearing. So I used the debugger and found out that the error node element at the top of the js. code was successfully finding the element in the HTML and also successfully changing it's properties. And yet nothing was happening. So after a bit of research I found out that there is a property called isConnected which shows if the Node is connected to the Document. Turns out mine was connected(true) immediately after finding the element, but it was disconnected(false) by the time it entered the errorHandler() func. So again after a bit of debugging I found out that the bottom line of the onPageLoad func was causing the problem. It used to be rootUl.innerHTML += template(countriesObj); but that was breaking it. However, when I moved my error element in the HTML from the ul to outside the id="root" div, it was working fine. It was only breaking when the element was inside the ul. At the end of the day I fixed it by using Element.insertAdjacentHTML() instead, which wouldnt sever the connection between the error element and Document. So after about an hour of struggle, I am curious why that happens and what the difference is between rootUl.innerHTML += template(countriesObj); and rootUl.insertAdjacentHTML('beforeend', template(countriesObj));
Just to add, rootUl.innerHTML+=template(countriesObj) breaks the connection even when used by other function which are not shown here.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List Towns</title>
<script src="../handlebars.min.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<p style="text-align: center; font-size: 18px; font-weight: 700; font-family: Arial, Helvetica, sans-serif;">
Input in the following format: "{Country}: {town}, {town} {Country}: {town}, {town}" and so on. Matching is case-insensitive. Non-english characters are not supported. Numbers are not supported. Check regex for more info. Reset button deletes entire database and refreshes page.
</p>
<form action="#" class="content">
<label for="towns">Towns</label>
<input id="towns" type="text" placeholder="ex. Bulgaria: Burgas, Varna Turkey: Ankara"/>
<button id="btnLoadTowns">Load</button>
</form>
<div id="root">
<ul>
<!--When the element was here, innerHTML wasn't working correctly-->
<h4 id="error" style="color: rgb(136, 9, 0); display: none;" >Error. Input string not in correct format. Look at instructions above.</h4>
</ul>
</div>
<!--When the element was here, innerHTML was working fine-->
<!-- <h4 id="error" style="color: rgb(136, 9, 0); display: none;" >Error. Input string not in correct format. Look at instructions above.</h4> -->
<button id="reset">Reset</button>
<h4 id="empty-database" style="color: rgb(136, 9, 0); display: none;" >Database is currently empty.</h4>
</body>
<script src="./app.js"></script>
</html>
async function pageApp(){
//Misc
let error = document.querySelector('#error');
let emptyDatabase = document.getElementById('empty-database');
// Grab the unordered list of countries
let rootUl = document.querySelector('#root ul');
// Extract(GET request) data from database
let database = await getRequestForCountries();
// Get the two templates: One is for both country and town, another is just for town when country already exists
let template = await getTemplate();
let templateTown = await getTemplateTown();
// Load countries on page load
onPageLoad();
//Attach load event to button
attachLoadEvent();
//Reset button for deleting the database
resetButton()
function errorHandler(){
error.style.display = 'block';
setTimeout(function(){
error.style.color = 'rgb(136, 9, 0)';
error.style.background = 'none';
}, 150)
error.style.color = 'red';
error.style.background = 'rgb(136, 9, 0)';
}
function onPageLoad(){
database.forEach(entry => {
let townsArr = entry.towns;
let countryName = entry.countryName;
let townsArrObj = townsArr.reduce((acc, cur) =>{
let townObj = {
name: cur
}
acc.push(townObj);
return acc;
},[]);
let countriesObj = {
countries:[
{
name: countryName,
towns: townsArrObj
}
]
}
//Was rootUl.innerHTML += template(countriesObj); But that breaks the DOM of error and makes error.isConnected = false;
// rootUl.innerHTML += template(countriesObj);
rootUl.insertAdjacentHTML('beforeend', template(countriesObj));
})
}
Element.innerHTML +=, gets HTML code within the element and append it with something.
document.querySelector('p').innerHTML += `<span>Appended span</span>`;
<p>
Lorem Ipsum
<span style="color: red">Something</span>
<p>
Whereas, Element.insertAdjacentHTML('beforeend', 'To be inserted node'), will add new node, before the specified element.
document.querySelector('p').insertAdjacentHTML('beforeend', '<div>I am div</div>');
<p>Lorem ipsum</p>
Related
I'm trying to create a responsive sidebar for processing orders by displaying items from an array one at a time.
Ideally, one item is displayed and the user can choose from three separate actions: Previous, Select, and Next.
The information in the array is pulled from order data within the workbook, this isn't the exact code I'm using but its written to present the same functionality:
function pullData() {
const sheet = ss.getSheets()[0];
const column = {
orderNumber: 1,
customerName: 2,
quantityOrdered: 3
}
// finding all instances of orders for product ID: XY1234
const productId = 'XY1234';
const orders = sheet.createTextFinder(productId).matchEntireCell(true).findAll();
// array to store all orders for this product
const orderInfo = new Array();
orders.forEach(order => {
// collecting order info
let row = order.getRow()
let orderNumber = sheet.getRange(row, column.orderNumber);
let customerName = sheet.getRange(row, column.customerName);
let quantityOrdered = sheet.getRange(row, column.quantityOrdered);
// combining the order info into a list item
let thisOrder = `
Order number: ${orderNumber}
Customer name: ${customerName}
Quantity ordered: ${quantityOrdered}`;
orderInfo.push(thisOrder);
});
}
and this is a simple idea of the HTML:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
div.listContent {
margin-left: 15%;
text-align: left;
}
div.buttons {
margin: auto 10% auto 10%;
display: flex;
justify-content: space-between;
ailgn-items: center;
}
.input {
padding: 8px;
border-radius: 15px;
border: 2px solid black;
}
</style>
</head>
<body>
<div class="listContent">
<!-- this is how orderInfo[x] would be displayed, formatting aside -->
<p>
Order number: [order number]<br>
Customer name: [customer name]<br>
Quantity ordered: [quantity ordered]
</p>
</div>
<div class="buttons">
<input class="input" type="button" onclick="" value="Previous">
<input class="input" type="button" onclick="" value="Select">
<input class="input" type="button" onclick="" value="Next">
</div>
</body>
</html>
I lack knowledge in frontend at the moment &, given that I haven't been able to display different items from orderInfo to the sidebar, I haven't been able to troubleshoot ideas.
My current thoughts are decrementing the index in orderInfo when the "Previous" button is clicked and incrementing the index respectively; however that's neither here nor there for now.
The program is written modularly so I've tried some other individual functions but the problem with each attempt is that I can't just insert a variable from a .gs file to the HTML without it being written into the HTML.
I'll update if I figure it out
You can add id to some tag, e.g. <p id="content"> and change it dynamically using innerHtml.
And you can pass data from GAS into javascript with plain JSON conversion, something like this
In html
...
<div class="listContent">
<!-- this is how orderInfo[x] would be displayed, formatting aside -->
<p id="content"> <!-- NOTICE ID ADDED !!! -->
...
<input class="input" type="button" onclick="next" value="Next">
<input class="input" type="button" onclick="prev" value="Prev">
<input class="input" type="button" onclick="select" value="Select">
...
<script type="text/javascript">
var index = 0;
var data = JSON.parse(<?= JSON.stringify(data) ?>
function next() {
if(index<data.length - 1) index++;
setContent();
}
function prev() {
if(index>0) index--;
setContent();
}
function select() {
// Whatever
}
function setContent() {
document.getElementById('content').innerHtml = data[index];
}
</script>
In apps script:
While template rendering add appropriate data from spreadsheet, as simple array, for example
var template = HtmlService.createTemplateFromFile('index');
template.data = pullData();
return template.evaluate().getContent()
You also need to update your pullData to return value in the end
...
return orderInfo;
}
Let's say I want to append a pattern like this in the DOM:
*
* *
* * *
* * * *
My JavaScript code looks like this:
function tri1()
{
let rows=document.getElementById("upperTri").value;
for(let i=1; i<=rows; i++)
{
for (let j=1; j<=i; j++)
{
document.getElementById("resTri1").appendChild(document.createTextNode(star));
}
document.getElementById("resTri1").appendChild(document.createElement("br"));
}
}
var star="*";
The thing is that I want to insert em space after each asterisk printed in the pattern, but HTML doesn't let one add more than one space unless it's an , etc. But I couldn't find any way to appendChild() the emspace in the DOM. It just prints the text instead of the em space.
Any solution to this?
My HTML code:
<html>
<head>
<title>Pattern</title>
<script type="text/javascript" src="PB220322.js"></script>
</head>
<body>
<h1>JS Lab Session 22-03-22</h1>
<fieldset>
<legend><h2>Left Triangle</h2></legend>
<label for="upperTri">Enter the number of rows:</label>
<input type="number" name="upperTri" id="upperTri" onchange="tri1()">
<input type="submit" value="Enter" onsubmit="tri1()">
<div id="resTri1"></div>
</fieldset>
</body>
</html>
Append to the innerHTML of the parent element after appending the text node:
function tri1()
{
let rows=document.getElementById("upperTri").value;
for(let i=1; i<=rows; i++)
{
for (let j=1; j<=i; j++)
{
let elem = document.getElementById("resTri1")
elem.appendChild(document.createTextNode(star));
elem.innerHTML += " "
}
document.getElementById("resTri1").appendChild(document.createElement("br"));
}
}
var star="*";
<html>
<head>
<title>Pattern</title>
</head>
<body>
<h1>JS Lab Session 22-03-22</h1>
<fieldset>
<legend><h2>Left Triangle</h2></legend>
<label for="upperTri">Enter the number of rows:</label>
<input type="number" name="upperTri" id="upperTri" onchange="tri1()">
<input type="submit" value="Enter" onsubmit="tri1()">
<div id="resTri1"></div>
</fieldset>
</body>
</html>
Really quick:
Inline event handlers are garbage so in the example that onchange and onsubmit have been replaced with .addEventListener().
.createTextNode() is quite antiquated, albiet very stable, it renders text not HTML. .innerHTML and .textContent can destroy content if it isn't appended with a += operand. There is a method that isn't destructive and very versatile: .insertAdjacentHTML(). The Unicode characters you are trying to use can be in different formats:
// HTML Decimal & Hexidecimal
*
// CSS
\00002a\002003
// JavaScript
\u002a\u2003
* needs to be rendered as HTML so methods such as .insertAdjacentHTML() is ideal, but more often you'll see .innerHTML being used despite it's limitations.
The example below employs some useful interfaces from the HTML DOM API. There are extensive details commented in the example -- the following list are references pertaining to those comments:
HTMLFormElement
HTMLFormControlsCollection
HTMLFormElement Submit Event
Event.preventDefault()
Events
Event Delegation
Note: in the example the "stars" are just a fancier asterisk ❉.
// Event handler passes the Event Object
function tri(e) {
/*
Stop the default behavior of form#UI✺ during a "submit" event.
*/
e.preventDefault();
// Create a HTMLFormControlsCollection🞲 (>this< is form#UI)
const IO = this.elements;
/*
The value of input#rQty coerced into a real number
(prefixed with a plus '+' will coerce a string into number)
*/
const rows = +IO.rQty.value;
// ❉ and emspace in HTML decimal format;
const star = '❉ ';
for (let i = 1; i <= rows; i++) {
for (let j = 1; j <= i; j++) {
/*
iAHTML() renders htmlString into real HTML without destroying
content (unlike .innerHTML)
*/
IO.box.insertAdjacentHTML('beforeEnd', star);
}
IO.box.insertAdjacentHTML('beforeEnd', '<br>');
}
}
/*
Bind form#UI to the "submit" event.
When "submit" event fires, the event handler tri(e) will be invoked.
Note the terse syntax of the DOM Object:
document.forms.UI
this is from the HTMLFormElement interface.
*/
document.forms.UI.addEventListener('submit', tri);
/*
✺ form#UI is also >this< within the event handler because it is registered
to the "submit" event.
*/
/*
🞲 HTMLFormControlsCollection is part of the HTMLFormElement interface. It
is an array-like object consisting of all form controls under form#UI. Form
controls are:
<button>, <fieldset>, <input>, <object>, <output>, <select>, <textarea>
form controls may be referrenced by id or by name.
*/
html {
font: 2ch/1.15 'Segoe UI';
}
header {
margin-bottom: -12px;
}
h1 {
margin-bottom: 0;
}
fieldset {
min-width: max-content;
}
legend {
margin-bottom: -12px;
}
label,
input {
display: inline-block;
font: inherit;
margin-bottom: 4px;
}
#rQty {
width: 5ch;
text-align: right;
}
.box {
font-family: Consolas;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>JS Lab Session 22-03-22</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
/* Any CSS here has second highest priority */
</style>
</head>
<body>
<form id='UI'>
<header>
<h1>JS Lab</h1>
<p>Session 22-03-22</p>
</header>
<fieldset>
<legend>
<h2>Left Triangle</h2>
</legend>
<label for="rQty">Enter the number of rows: </label>
<input id="rQty" type="number" min='3' max='100' value='3'>
<input type="submit" value="Enter">
<fieldset name="box" class='box'></fieldset>
</fieldset>
</form>
<script>
/*
JavaScript can go here.
Place all external script tags directly above/before this
script tag.
*/
</script>
</body>
</html>
Having a very weird issue with a simple div visibility toggle script.
I'm just using javascript to switch a div between 'display: block' and 'display: none' to toggle its visibility. Very routine stuff.
And in general it works, but it always fails on the first click after a fresh page load. Then it works consistently from the second click onward.
No error output on console.
Relevant HTML:
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="res/classes.js"></script>
<script type="text/javascript" src="res/util_c.js"></script>
<script type="text/javascript">
// load client prefs
var clientPrefs = new ClientPrefs();
</script>
</head>
<body>
<a id="join_show_publist" class="a_btn" onClick="javascript:joinPublistToggle()">View Public Matches</a><br />
<!-- list of public games -->
<div id="join_publist_container" class="ovr">
<table id="join_publist_listbox">
<tr id="join_publist_listbox_header">
<td>Table Name</td>
<td>Open</td> <!-- open seats remaining at table -->
</tr>
</table>
<div class="spacer"></div>
<div id="join_savePref_container">
<input id="join_savePref" type=checkbox onclick="javascript:clientPrefs.joinAlwaysShowPubToggle()" />
<span id="join_savePref_label" onclick="javascript:clientPrefs.joinAlwaysShowPubToggle()">Always show public tables list</span>
</div>
</div>
Relevant CSS:
div.ovr {
display: none;
}
...and finally in util_c.js:
// toggle visibility of public tables list
function joinPublistToggle() {
var listContainer = document.getElementById('join_publist_container');
if (listContainer.style.display == 'none') {
listContainer.style.display = 'block';
} else {
listContainer.style.display = 'none';
}
}
First click: nothing happens.
Second click: the DIV is shown
Third click: the DIV is re-hidden
etc..
If I put an alert(listContainer.style.display) into the joinPublistToggle function, the alert comes up empty with the first click, then shows 'none' with the second click.
But the CSS specifically sets the display style for that div as 'none' on load. And if I look at that div in the page inspector after a fresh page load the inspector specifically says the div's display property is set to none.
So the issue seems to be that javascript is reading that property as empty even though that property is set as 'none'.
Why would it do that?
style returns the inline style of the element, and your element doesn't have any, which is why listContainer.style.display returns an empty string and the condition fails.
It would work if you compared against 'block' instead but it's not really more reliable.
function joinPublistToggle() {
var listContainer = document.getElementById('join_publist_container');
if (listContainer.style.display == 'block') {
listContainer.style.display = 'none';
} else {
listContainer.style.display = 'block';
}
}
https://stackoverflow.com/questions/69213611/js-onclick-event-ignores-first-click-works-for-every-subsequent-click/69224191#
The other answers provide valid solutions, here is another using classes:
CSS:
div.hidden {
display: none;
}
HTML:
<div id="join_publist_container" class="ovr hidden">
(of course you can also just keep using ovr but I wasn't sure what that's for)
JS:
function joinPublistToggle() {
document.getElementById('join_publist_container').classList.toggle('hidden');
}
And in general it works, but it always fails on the first click after a fresh page load. Then it works consistently from the second click onward.
I am going to asume when you clicked the link, the checkbox and the table should go away. And when it is clicked again, the table and the checkbox should show. I modified your code and it works for me.
for your HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<script type="text/javascript" src="classes.js"></script>
<script type="text/javascript" src="util_c.js"></script>
<script type="text/javascript">
// load client prefs
var clientPrefs = new ClientPrefs();
</script>
</head>
<body>
<a id="join_show_publist" class="a_btn" onClick="javascript:joinPublistToggle()">View Public Matches</a><br />
<!-- list of public games -->
<div id="join_publist_container" class="ovr">
<table id="join_publist_listbox">
<tr id="join_publist_listbox_header">
<td>Table Name</td>
<td>Open</td> <!-- open seats remaining at table -->
<td>Starts</td> <!-- time left until game starts (or "started" if underway) -->
<td>Timer</td> <!-- time limit for turns (or "none") -->
<td>Min</td> <!-- min players to start the round (or '--' if already underway) -->
<td>Late</td> <!-- whether late joiners are allowed at the table -->
<td>AI</td> <!-- whether there are any AI players at the table (and if so, how many)
also colour denotes difficulty: green-easy / yellow-med / red-hard -->
</tr>
<!-- Generate list via js. Clicking any list entry joins -->
</table>
<div class="spacer"></div>
<div id="join_savePref_container">
<input id="join_savePref" type=checkbox onclick="javascript:clientPrefs.joinAlwaysShowPubToggle()" />
<span id="join_savePref_label" onclick="javascript:clientPrefs.joinAlwaysShowPubToggle()">Always show public tables list</span>
</div>
</div>
Classes.js:
// ClientPrefs - client-side preferences
class ClientPrefs {
constructor() {
// JOIN GAME page settings
this.joinAlwaysShowPub = false;
}
joinAlwaysShowPub() { return joinAlwaysShowPub; }
joinAlwaysShowPubToggle() {
// toggle setting in memory
this.joinAlwaysShowPub = !this.joinAlwaysShowPub;
// update checkbox & label
document.getElementById('join_savePref').checked = this.joinAlwaysShowPub;
}
}
And finally your other script:
function joinPublistToggle() {
var listContainer = document.getElementById('join_publist_container');
if (listContainer.style.display == 'none') {
listContainer.style.display = 'block';
} else {
listContainer.style.display = 'none';
}
}
Here are few reasons why your code might not work:
I think the problem is that you mistyped joinPublistToggle() to joinShowPubList.
Your div has a value of nothing for the display property. So, when JS looks at your div, well, the div is not set to none or block, I don't know how to handle it. After you clicked the link a second time, it sets the display in your JS code. So, it knows how to handle it.
Maybe add an display property to your a tag and set it to block so JS know what the property of the style is.
<a id="join_show_publist" class="a_btn" onClick="javascript:joinPublistToggle()" style="display:block;">View Public Matches</a><br />
This doesn't really answer my question, but I've implemented a simple workaround by adding an OR statement into the JS function:
function joinPublistToggle() {
var listContainer = document.getElementById('join_publist_container');
if ( (listContainer.style.display == 'none') ||
(listContainer.style.display == '' ) ) {
listContainer.style.display = 'block';
} else {
listContainer.style.display = 'none';
}
}
This doesn't explain why it was behaving so odd, and it isn't a proper solution (as a proper solution would address the cause, not the symptom).
But it works.
I won't mark the post as solved just yet in case any wizards end up reading this and are able to explain why the problem occurred in the first place.
I am trying to make an emoji system whereby a file called emoji.css willm store the emojis.The names of all the emoji's are stored in a really javascript array although slightly altered.When users input a emoji text
(something like :emoji: ,:another-emoji:)Javascript should check if it that text is in the emoji array,if it is,it will be automatically turned into an emoji.
Atleast that is what I'm trying to do
This is the steps of what is supposed to happen
Page loads
User inputs in input with class 'input'
If the text is in the array called
emoji,javascript
i.Alerts the name of the input
ii.Says 'it is in array'
iii.Copy the text into a div with class
`see`
iv.Text in div automatically becomes an array`
And I think that is where the problem is.
In the emoji.css file all emoji have classes with names like em em-abc,em em-woman but when users want to call an emoji, they must input a text with a : in the front and back like :abc:,:woman: so jquery should automatically change that input string(:abc:) to emoji.css class(em em-abc) and I used this line of code to do that
$(".see").addClass("em em-"+$(".see").html().split(":").pop()).removeClass(".see");
here is my full code:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rad emojis</title>
<style>
div {
color: blue;
}
span {
color: red;
}
</style>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<link href="emoji.css" rel="stylesheet">
</head>
<body>
<div class ="see em-abc"></div>
<input class="input">
<div class ="see"></div>
<div class ="see2"></div>
<div class ="see3"></div>
<div class="add"></div>
<script>
var emoji = [ ":abc:", ":woman",":eye:", <!--it was much longer than this--> ];
var input=$(".input");
var input2=$(".input").val();
$(input).change(function(){
$(".see").html( $(".input").val());
if(jQuery.inArray($(".input").val(), emoji) != -1) {
var see="."+$(".see").html();
$(".see2").html(see);
var classs =see+"";
alert($(".see").html());
var real=$(".see").html().split(":").pop();
$(".see3").html(real);
$(".see").toggleClass("em em-"+$(".see").html().split(":").pop());
$(".see").addClass("em em-"+$(".see").html().split(":").pop()).removeClass(".see");
alert("is in array");
alert($(".see").html());
} else {
alert("is NOT in array");
}
});
</script>
</body>
</html>
When you remove a class using the removeClass method, you have to omit the dot.
Simply use
$(".see").addClass("em em"+$(".see").html().split(":").pop()).removeClass("see");
I have a page which has a lot of buttons. What I need to do is to show a div near the button. I tried this:
<style>
#noteDiv {display:none; position: absolute; background-color: white; border: 1px solid blue;}
</style>
<script>
function showNote(e) {
var x = 0, y = 0;
if (!e) e = window.event;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
}
else if (e.clientX || e.clientY) {
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
document.getElementById("noteDiv").style.display = "block";
document.getElementById("noteDiv").style.left = (x)+"px";
document.getElementById("noteDiv").style.top = (y-350)+"px";
}
function hideNote() {
document.getElementById("noteDiv").style.display = "none";
}
</script>
<body>
<?php
echo "<button type ='button' id = 'noteButton'>Note</button>";
echo "<script>document.getElementById('noteButton').onclick = showNote;\n";
echo "</script>";
?>
</body>
<div id='noteDiv' >
<div ><span onclick="hideNote()">Close</span></div>
<br clear="all">
<div id='noteContent' style='max-height: 30em'></div>
</div>
It does work. But sometimes the page will show one more div on top of the page, like a warning message, and thus the noteDiv's position will be far from the buttons to which it should attach.
My thinking is to get the position of the buttons, and send the x, y values of the button position to the function showNote(), from there show the noteDiv. I don't know if this idea is reasonable and how to get and transfer the current clicked button's position to javascript?
Any suggestions and hints will be appreciated!
From the beginning.
Load javascript on top of your page is a very bad idea. I'll let the tons of web articles to explain you why. Just to say one reason, the js files are downloaded before the html is rendered (depending on the browser), resulting in a slower rendering of the page.
About your approach:
Three words: separation of concerns. Positioning dom elements is not what belongs to javascript (except some very rare occasions).
Styling the DOM, which comprehends positioning of the objects, belongs to the Cascading Style Sheet, also known as CSS.
So if something is not rendered in the right way, don't try to fix it with javascript. It will only drives you to enormous headaches.
For a better answer, please provide a code that can show us the error.
UPDATE
Here is a working example (probably not optimised) of what you are maybe trying to achieve. Please, please, please, please... read a book about html, css and js. It's totally worth it. I didn't use php, didn't need it.
Just for the records, the general structure of an html page I personally use is like this one:
html
head
title
meta
styles link
styles sections
js **LIBRARIES** which need to be loaded on **TOP**
google analytics
body
html content
js **LIBRARIES** which need to be loaded on **BOTTOM**
js scripts
And for your sanity, and of the people who helps you, indent correctly (it's also a sign of respect to the people who are reading your code).
Here is the code with the snippet:
function toggleNote(id) {
var noteParent = document.getElementById(id);
var note = noteParent.querySelector('.note');
var display = "none";
if (note.style.display == "none" || note.style.display == "" ) {
display = "block";
}
note.style.display = display;
}
.note {
display: none;
position: relative;
background-color: white;
border: 1px solid blue;
}
.noteContent {
max-height: 30em
}
<body>
<div class="buttonContainer" id="note0">
<button id='noteButton' onclick="toggleNote('note0')">Note</button>
<div class='note'>
<div>
<button onclick="toggleNote('note0')">Close</button>
</div>
<br clear="all">
<div class='noteContent'>It's something!</div>
</div>
</div>
</body>
All the HTML like <button> <div> <span> <ul><li> <table> etc MUST be inside the <body> </body> tag:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<!-- you can include css and javascript here -->
<!-- but best place to include javascript ist at the bottom -->
<!-- see last comment -->
</head>
<body>
<?php echo '<button type="button" id="noteButton">Note</button>'; ?>
<!-- best place to include javascript or echo them with PHP what ever
right before the closing body tag -->
</body>
</html>
You are echoing a <button> via PHP before the opening <body> tag which is wrong. Try use something like firebug and https://validator.w3.org/