'Why does redrawing my image not work properly?
You helped me get the draw function work properly. Here's the complete program, which works perfectly until the "draw" button is clicked. Instead of drawing a new image, the entire window vanishes. Any help greatly appreciated!
This code is formatted as instructed, 4 spaces per tab. I don't know any more details to add.
<!DOCTYPE html>
<html>
<head>
<title>Digital Holograms</title>
<style>
div {
float: left;
height: 580px;
width: 500px;;
padding: 0 10px;
}
#column1 {
background-color: #FFFFFF;
}
#column2 {
background-color: #FFFFFF;
width: 500px
}
</style>
</head>
<body>
<div id="column1">
<canvas id="my-canvas" width="500" height="500"></canvas>
<script>
let selFun = 0 // selected function to draw
// user may change this by selection box
let canvas = document.querySelector('#my-canvas')
let context = canvas.getContext('2d')
context.fillStyle = 'black'
function draw(n) {
let contourInterval = 1500
let contourWidth = 500
let fsize = 1000
let x0 = fsize / 2
let y0 = fsize / 2
for (j = 0, y = -y0; j < fsize; j++, y++) {
for (i = 0, x = -x0; i < fsize; i++, x++) {
let fun = 0
switch(n) {
case 0:
fun = (x*x + y*y)
break;
case 1:
fun = (x*x - y*y)
break;
default:
fun = (x*x + y*y)
}
if (Math.abs(fun % contourInterval) < contourWidth) {
context.fillRect(x+x0/2, y+y0/2, 1, 1)
}
}
}
document.write('<h4 style="text-align: center;">Hologram of z = x² + y²</h4>');
}
draw(selFun)
function getSelection() {
let selFun = document.getElementById("selectFunction").value
//document.write(selFun)
}
</script>
<h3>To view other holograms:</br>Select a function, then press draw</h3>
<select id="selectFunction" onchange="getSelection()">
<option value=0>z = x² + y²</option>
<option value=1>z = x² - y²</option>
</select>
<button type = "button";
onclick = draw(selFun)>Draw</button>
</div>
</body>
</html>
Solution 1:[1]
There a several glitches that are preventing this code working as intended.
1) The loss of content on button press
This is a result of an incorrect use of document.write() (which can only be effectively used by inline scripts to inset text as the page is loading. Running it on a loaded page causes document.open() to be executed, clearing the existing document. See: https://developer.mozilla.org/en-US/docs/Web/API/Document/write
The correct way to update information on the page is to change the innerText or innerHTML of an existing element, or insert a new element with the required text.
In my snippet I added the h4 tag, with an id of updatedTitle, to the html (initially with no content, which will be added by the script):
<h4 id="updatedTitle" style="text-align: center;"></h4>
The innerText of the tag is added by the last line of the draw() function:
document.getElementById("updatedTitle").innerText = `Hologram of ${equation}`;
Note, the template literal ${equation}, this was used to allow the equation to be updated when a new option is drawn, by fetching the markup for the equation from the element (shown later).
2) Drop Down changes not registering
After correcting point 1), there was still a problem: pressing the draw button failed to render a new graph. After some investigation with console.log()s at various parts of the execution cycle I determined that the onchange of the dropdown element was not being received by the javascript.
The reason for this turned out to be a case of very bad luck - the choice of the function name intended to handle the change: getSelection. It turns out that getSelection is a built-in method of the Window object and seemingly cannot be used as a user function name.
Changing the function name to readSelection() cured this problem;
(https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection)
3) Button onclick event argument not accessible
The button's onlick event calls the draw() function, and attempts to send selFun as an argument. As written, selFun is not accessible from the element.
Instead, the onlick was changed to simply call the draw() function without any argument.
selFun is declared, and assigned with 0, in the global scope of the script and so is accessible from function in the script. Thus the argument n used in the switch block of the draw() function can be replaced directly with selFun (and n also deleted from the function parameter):
4) modification to readSelection()
The original values of the option list items were 0 and 1, entered as (unquotted) numbers in the markup. Although this is allowed, the 0 and 1 are converted to strings when the value attribute is read regardless of whether they were quotted or not (much like the contents of a text input field). Javascript is good at coercing numbers from digit characters but I was getting a bug where selFun was not being read properly for the case statement. Formally setting selFun to an integer value cured this:
selFun = parseInt(document.getElementById("selectFunction").value);
Since readSelection is invoked each time the drop down changes, it is also the place to keep track of which equation should be used in the label on next draw. Hence the following line was included in the function:
equation = document.getElementById("selectFunction").children[selFun].innerText;
with equation being declared (and set to the dropdown initial state) in the global scope of the script, to be read and used to form a label, as discussed in 1) above.
5) Draw only working for first change of equation
Once the above changes were made, changing the dropdown and pressing the button resulted in the new equation being rendered. However, subsequent changes and attempts to draw did not appear to do much.
This was because the second equation was being drawn on top of the first and, once both equations were drawm further drawing didn's show any change.
The solution was to include a standard clear step at the start of the draw() function:
context.clearRect(0, 0, canvas.width, canvas.height);
6) Element accessibility The entire script was moved to the foot of the html to allow all elements to load before running the script, this prevented some errors where the js was trying to access elements that did not yet exist.
The programme seems to work as intended now. I don't understand anything about the equation plots but they are most impressive and, should you add more to it in future, perhaps you will comment back here so I can have a look!
Working snippet:
let selFun = 0
let equation = document.getElementById("selectFunction").children[selFun].innerHTML; // to be used to update graph label;
let canvas = document.querySelector('#my-canvas');
let context = canvas.getContext('2d');
context.fillStyle = 'black';
function draw() {
// clear canvas'
context.clearRect(0, 0, canvas.width, canvas.height);
let contourInterval = 1500;
let contourWidth = 500;
let fsize = 1000;
let x0 = fsize / 2
let y0 = fsize / 2
for (j = 0, y = -y0; j < fsize; j++, y++) {
for (i = 0, x = -x0; i < fsize; i++, x++) {
let fun = 0
switch(selFun) {
case 0:
fun = (x*x + y*y);
break;
case 1:
fun = (x*x - y*y);
break;
default:
fun = (x*x + y*y);
} // end switch
if (Math.abs(fun % contourInterval) < contourWidth) {
context.fillRect(x+x0/2, y+y0/2, 1, 1)
} // end if;
} // next i;
} // next j;
const titleH4 = document.getElementById("updatedTitle");
document.getElementById("updatedTitle").innerText = `Hologram of ${equation}`;
} // end draw;
draw();
function readSelection() {
selFun = parseInt(document.getElementById("selectFunction").value);
equation = document.getElementById("selectFunction").children[selFun].innerText;
} // end readSelection function;
div {
float: left;
height: 580px;
width: 500px;;
padding: 0 10px;
}
#column1 {
background-color: #FFFFFF;
}
#column2 {
background-color: #FFFFFF;
width: 500px
}
select {
width: 200px;
height: 1.5em;
font-size: 2em;
border-radius: 5px;
background: cyan;
text-align: center;
}
button {
width: 100px;
height: 1.5em;
font-size: 2em;
}
<div id="column1">
<canvas id="my-canvas" width="500" height="500"></canvas>
<h3>To view other holograms:</br>Select a function, then press draw</h3>
<select id="selectFunction" onchange="readSelection()">
<option value="0">z = x² + y²</option>
<option value="1">z = x² - y²</option>
</select>
<button type = "button" onclick="draw()">Draw</button>
<h4 id="updatedTitle" style="text-align: center;"></h4>
</div>
edit
Regarding the appearance of the dropdown and button - they can be styled much like any other html element. I've added some arbitrary changes to their apperance by adding rules for select and button elements to the css. The elements can be given a class name if more specific styling is needed without affecting other elements of the same kind.
Solution 2:[2]
I don't know what your code is trying to do but when the button is pressed you are updating the source completely.
Here is that bad code :)
document.write('<h4 style="text-align: center;">Hologram of z = x² + y²</h4>');
Add it inside a div instead of writing it to the whole document.
document.getElementById("example").innerHTML = 'your codes';
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Riga |
