Learning Web 04: Divs and SVG
After running into issues last time I did some more research and believe there’s a way better direction to take. I won’t say that was a waste of time because I learned some stuff that may very well help later. I’m pretty sure this is going to be more fruitful though.
Attempt to do it here
I should be able to create an outer div, within which I’ll create my other stuff, and then be able to assign it some real-estate here and do it in the blog like the first one because I’m not doing any selenium testing this time. So here I’ll try to just create one that has a border and some area:
So far so good…SO WHAT!! (sorry, great album – not a fan anymore, but yeah)
It was easy except at first I expected I could just say height: 200; and it would respond.
It didn’t so messing with that and searching why took a sec (some 10 minutes) and sent
me on a bit of a goose chase where I learned that you can’t set the height on an inline
element–but I don’t have one so that wasn’t actually the problem. The problem is simply that
it needed a unit: height: 200px; <- set to 200 pixels.
<div style="border: 1px solid #ffffff; width: 100%; height: 200px;" />
Node that can be focused
Now what I should be able to do is put another div inside of that one and set it to be the size of its content, stick some crap in it just to have something in there (it’ll have real stuff later), and then place it in the center of its container and it should draw there. Then I should be able to write some javascript that responds to it being clicked and actually highlights it.
If I can do this then it is a massive improvement to the canvas route. I was trying to think of how I’d keep state and I really wanted to ram it into the document instead of keep it around myself. With this approach I’m doing that. It will be way, way, WAY easier.
DERP!
OK, so that went pretty well. I wasn’t able to center it the way I wanted. I wanted to center the element within the container but you can only center the container’s content. The reason I wanted to won’t matter though–I wanted to keep the position as part of the element and I still will, centering things won’t be a common operaton and I guess I’ll calc it manually. Besides that though I was able to do the workaround for now and easily click the item after I used the right function name to add the event LISTENER and not the HANDLER.
<div id="derpvas0" style="border: 1px solid #ffffff; width: 100%; height: 200px; display: flex; align-items: center; justify-content: center;">
<div id="node0" style="border: 1px solid #cccccc; width: max-content;">HELLO<br><b>DERP!</b></div>
</div>
<script>
nd = document.getElementById("node0");
function click_handler(event) {
nd.style.border = "2px solid #ffffff";
}
nd.addEventListener("click", click_handler, false);
</script>
One of many
Let’s see if I can make a group of several such boxes de-select any other box when one is selected.
DERP!
DERP!
DERP!
DERP!
That was fun. So first I ran into trouble because my reset function didn’t work. I tried to iterate
the childNodes of the derpvas to set them all back to the original border. This was wrong
because I actually wanted children because I only want subelements and not words and stuff–I think.
Then children[i].style failed. I ended up with a range based for, which is odd because I couldn’t find
how to do that last time.
Then I’m doing the listener on the derpvas instead of the node so I needed to find the right thing
that got clicked. I did this by taking whatever the event target is, which is the smallest element
that contains the mouse, and asking it for the closest .node, which I’ve added as a selector by
making it the node divs’ class.
My click function then just resets all nodes to unselected, then selects the one that was clicked.
I should probably being doing this by adding and removing a class though. This makes it a trackable state and then the css for “selected” should do its job.
<div id="derpvas1" style="border: 1px solid #ffffff; width: 100%; height: 200px; display: flex; align-items: center; justify-content: space-evenly;">
<div id="node0" class="node" style="border: 1px solid #cccccc; width: max-content;">HELLO<br><b>DERP!</b></div>
<div id="node1" class="node" style="border: 1px solid #cccccc; width: max-content;">HELLO<br><b>DERP!</b></div>
<div id="node2" class="node" style="border: 1px solid #cccccc; width: max-content;">HELLO<br><b>DERP!</b></div>
<div id="node3" class="node" style="border: 1px solid #cccccc; width: max-content;">HELLO<br><b>DERP!</b></div>
</div>
<script>
function reset_derpvas(derpvas) {
let children = derpvas.children;
for (const child of children)
{
child.style.border = "1px solid #cccccc";
}
}
reset_derpvas(document.getElementById("derpvas1"));
function click_listener1(event) {
var derpvas = document.getElementById("derpvas1");
reset_derpvas(derpvas);
event.target.closest(".node").style.border = "2px solid #ffffff";
}
document.getElementById("derpvas1").addEventListener("click", click_listener1, false);
</script>
With classes and css
DERP!
DERP!
DERP!
DERP!
That was totally the way to do it. The split of the styling out of the structure/state lets me keep that “selected” state in the node it is attached to in a way that is easily queryable. All I have to do is change that state and the display stuff in the browser just adjusts. This is what I expected and what I want.
<style type="text/css" media="screen">
.derpvas {
border-color: #ffffff;
border-style: solid;
border-width: 1px;
width: 100%;
height: 200px;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.node {
border-color: #cccccc;
border-style: solid;
border-width: 1px;
}
.selected {
border-width: 2px;
border-color: #ffffff;
}
</style>
<div id="derpvas2" class="derpvas">
<div id="node0" class="node">HELLO<br><b>DERP!</b></div>
<div id="node1" class="node">HELLO<br><b>DERP!</b></div>
<div id="node2" class="node">HELLO<br><b>DERP!</b></div>
<div id="node3" class="node">HELLO<br><b>DERP!</b></div>
</div>
<script>
derpvas = document.getElementById("derpvas2");
function click_node(event) {
clicked = event.target.closest(".node");
selected = derpvas.getElementsByClassName("selected");
if (selected.length > 0) {
selected[0].classList.remove("selected");
}
if (clicked) {
clicked.classList.add("selected");
}
}
derpvas.addEventListener("click", click_node, false);
</script>
Key press
Let’s see if I can solve my original problem now. I originally had trouble getting the enter key to pop up the dialog based on the selected item. What happens now?
DERP!
DERP!
DERP!
DERP!
Select a node and then press a key to see what I did.
My problem was probably totally unrelated. I ended up making the document the listener for one. The “keydown” event for enter was getting filtered to set focus on the div when I used tabindex to enable having the derpvas the item with the listener.
Then it took me a while to notice that I succeeded. The issue is that the event to pop up the dialog also seems to get sent to the dialog that pops up, which is weird. It must be that somehow the handler for the button in the dialog is called after the handler for the document. I guess this kinda makes sense? Then since it’s now visible it will do something: close the dialog. Happens too fast to see unless you have it also popping up an alert at the same time.
<div id="derpvas3" class="derpvas" >
<div id="node0" class="node">HELLO<br><b>DERP!</b></div>
<div id="node1" class="node">HELLO<br><b>DERP!</b></div>
<div id="node2" class="node">HELLO<br><b>DERP!</b></div>
<div id="node3" class="node">HELLO<br><b>DERP!</b></div>
</div>
<dialog id="dialog0">
<p>DERP!!!!</p>
<button commandfor="dialog0" command="close">Close</button>
</dialog>
<script>
derpvas = document.getElementById("derpvas3");
dialog = document.getElementById("dialog0");
function key_listener(event) {
//if (event.key != "Enter") return;
selected = derpvas.getElementsByClassName("selected");
if (selected.length > 0) {
dialog.showModal();
}
}
function click_node2(event) {
clicked = event.target.closest(".node");
selected = derpvas.getElementsByClassName("selected");
if (selected.length > 0) {
selected[0].classList.remove("selected");
}
if (clicked) {
clicked.classList.add("selected");
}
}
derpvas.addEventListener("click", click_node2, false);
document.addEventListener("keydown", key_listener, false);
</script>
Done for night…
That was something like 2-3 hours. I should watch the clock doing this to see how long this learning is taking me.
What I discovered this time was that yeah, I can do this much cleaner with divs. Everything about it is better. I didn’t completely solve my enter key issues but I learned stuff about that also. I also realized that I’m going to have to be thinking about dialog behavior quite differently than I’m used to here–that enter pops up the dialog and closes the dialog thing is wackadoo but actually makes sense if you realize the dialog is always there trying to handle that event. That can’t be the complete story but I think it’s something like it. Reason it can’t is that I didn’t add any handler for the dialog, so that’s in its configuration either in making it a button or making it the close command or something–and it could be that this thing interacts differently than any handler I might attach. So, more to learn as time goes…
