haggis78 / BreconChurch

Files for our DH project on Henry VIII's Letter Patent founding Brecon Collegiate Church in Wales.
3 stars 0 forks source link

Text-wrapping for SVG Timeline #24

Closed ebeshero closed 4 years ago

ebeshero commented 4 years ago

@amberpeddicord I was looking at the SVG timeline with the JavaScript, and also thinking about flexible displays for various sizes of screen (including mobile). Take a look at the 1561-1567 date range: http://brecon.newtfire.org/html/Timeline.html . If the screen isn't very wide, you just can't read all the long text that scrolls on and on to the right. It would be nice to introduce some text-wrapping, but that's not natural to an SVG <text> element.

Fortunately, there's a solution that introduces HTML inside the SVG! It's an element in SVG called <foreignObject>, and inside it you switch over to HTML elements, with a namespace on them. Read about it here: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject

You could use this <foreignObject> element with its @x and @y attributes in just the same way you're setting the <text> element. But we will need to change your JavaScript and CSS to be showing and hiding the <foreignObject> elements instead. (You can still keep the same @class attributes we were applying--it's just changing the element name.) Notice that you can set a very specific @width and @height on your <foreignObject> and inside it the text in your HTML <div> element will wrap. In CSS I imagine you could set a border property on <foreignObject>, so that can give you something more to work with for styling your timeline.

amberpeddicord commented 4 years ago

@ebeshero I'm trying to use the <foreignObject> elements, but I'm getting an error message on my SVG file that says "Unexpected element 'foreignObject'". It's doing the same for my div elements. Is there any way to fix this?

ebeshero commented 4 years ago

@amberpeddicord I see your commit this morning with the <foreignObject> element in it, so I'll see if I can help. It's an oXygen error, right? I'll see what I can find out.

ebeshero commented 4 years ago

@amberpeddicord I notice that when I open the HTML file with the SVG (with <foreignObject> elements) embedded in it, oXygen is happy with the file. However, if I extract that SVG and open it by itself as an SVG file, that's when I see the error message. I think this isn't a problem for us because:

ebeshero commented 4 years ago

I wonder whether we should file a bug report with oXygen about <foreignObject> or open an issue on the oXygen email list? I'll ask around...oXygen's validation of SVG ought to recognize its own foreignObject element. But I wonder if it's only supposed to be valid in a context when SVG is embedded inside an HTML page?

amberpeddicord commented 4 years ago

@ebeshero I was wondering the same thing! As far as the javascript goes, I started messing around with changing the text elements in our javascript file as well, but it wasn't working. I have this code

window.onload = initialize;

function initialize() {
    var circleSelect = document.querySelectorAll('circle[id]');
    var ellipseSelect = document.querySelectorAll('ellipse[id]');
    for (var i = 0; i < circleSelect.length; i++)
    {
        circleSelect[i].addEventListener('mouseover', textShow, false);
    }
     for (var j = 0; j < ellipseSelect.length; j++)
    {
        ellipseSelect[j].addEventListener('mouseover', textShow, false);
    }
}

function textShow() {
    hide_last();
    var ts = document.getElementsByClassName(this.id.split("-")[1]);
    for (var i = 0; i < ts.length; i++){
        ts[i].style.display = "block";
    }
}

function hide_last() {
    var foreignObjects = document.querySelectorAll('foreignObject[class]');
    for (var i = 0; i < texts.length; i++){

    foreignObjects[i].style.display = "none"
    }
}

and it wasn't working, so I was wondering what I did wrong here. Thanks!

amberpeddicord commented 4 years ago

@ebeshero Would it be an issue with having <div> elements inside the foreign object elements? Should I put the class attribute onto those or should I use the foreignObjects ones?

ebeshero commented 4 years ago

@amberpeddicord Yes--that was exactly what I'd recommend doing: Put the @class on the <div> elements which are literally what you'll be showing and hiding. I think the <foreignObject> element simply permits a changeover to HTML elements, and is not itself a display element. Let's see if that works?

amberpeddicord commented 4 years ago

@ebeshero The javascript still isn't working, did I do it correctly:

window.onload = initialize;

function initialize() {
    var circleSelect = document.querySelectorAll('circle[id]');
    var ellipseSelect = document.querySelectorAll('ellipse[id]');
    for (var i = 0; i < circleSelect.length; i++)
    {
        circleSelect[i].addEventListener('mouseover', textShow, false);
    }
     for (var j = 0; j < ellipseSelect.length; j++)
    {
        ellipseSelect[j].addEventListener('mouseover', textShow, false);
    }
}

function textShow() {
    hide_last();
    var ts = document.getElementsByClassName(this.id.split("-")[1]);
    for (var i = 0; i < ts.length; i++){
        ts[i].style.display = "block";
    }
}

function hide_last() {
    var divs = document.querySelectorAll('div[class]');
    for (var i = 0; i < divs.length; i++){

    divss[i].style.display = "none"
    }
}

I noticed that on the timeline page, the javascript for the menu bar also isn't working, so it may be an issue with how I'm associating it.

ebeshero commented 4 years ago

@amberpeddicord Have you checked to update your CSS file about the show / hide properties and whether they're updated for your new elements on the page?

ebeshero commented 4 years ago

Also, sorry for my delay!

amberpeddicord commented 4 years ago

@ebeshero I checked, but the properties that I had didn't need to be changed because I only moved the class attribute, so they should still be working. What updates should I try making?

ebeshero commented 4 years ago

@amberpeddicord I'm not sure without taking a closer look at the code. Here's a question: does the other JS work on the page you're developing? You mentioned earlier that none of the JS files were working and that might be a clue. I wonder if...

Can you push the latest stage of all this code together? I'll pull it in and see what I can see.

ebeshero commented 4 years ago

@amberpeddicord By the way, I strongly suspect it's the combination of different JS files that's to blame here. I've seen that as a problem in other projects before now.

ebeshero commented 4 years ago

One way to test the first possibility is to remove the other JS files and apply them one at a time to see if they work by themselves. Maybe start there.

amberpeddicord commented 4 years ago

@ebeshero We have a merge conflict! How do I resolve those again?

ebeshero commented 4 years ago

Let's see...First of all, add and commit your files to your own local computer so you don't lose your work.

ebeshero commented 4 years ago

Then, note what happens when you try to do a git pull. If we're lucky, git will know what to do and reconcile the new material with your local commit.

If we're unlucky, you'll have to do some manual editing to resolve an area of conflict in a file.

amberpeddicord commented 4 years ago

@ebeshero That worked! The updated files are pushed now.

ebeshero commented 4 years ago

@amberpeddicord Hmmm. Two questions: Did you push the Timeline.html file with this last commit? (Because what's on the repo that I see was updated 4 hours ago.)

Can you tell me about the status of the other JS files on the Timeline.html page? And have you tried isolating them to see if they work when the others are not associated with the HTML?

amberpeddicord commented 4 years ago

I did!

And I tried to isolate just the timeline.js file but that also didn't seem to work, either.

ebeshero commented 4 years ago

So, none of the JS files are working? Or just the one that goes over the SVG isn't working? (I know--I need to pull these in and open them up and take a look.)

amberpeddicord commented 4 years ago

The javascript for the menu bar is working on all of the other pages, so it's just an issue with the timeline page.

ebeshero commented 4 years ago

I'm working on this now. In the meantime, see the issue I posted about documenting the Brecon team's methodology!

haggis78 commented 4 years ago

I created (and thought I solved) a merge conflict some time after 5 PM. Sorry if I screwed it up. Mea culpa.

ebeshero commented 4 years ago

@haggis78 Try a git pull now and make sure all is well?

haggis78 commented 4 years ago

@ebeshero Looks good.

amberpeddicord commented 4 years ago

@haggis78 You're fine, this one was most definitely my fault! And it was a relatively easy fix, because we hadn't made changes to the same file.

ebeshero commented 4 years ago

Well! I have an update on your JavaScript. I found the tiniest of typos in the last function, an extra s in one line that I simply deleted. And lo, the JavaScript worked...but....not quite in the way we'd like. As soon as I load the page, I see the timeline. And as soon as my mouse visits a circle, the entire graph disappears. Yikes. And I see exactly why that's happening. Take a look at our code:

function hide_last() {
    var divs = document.querySelectorAll('div[class]');
    for (var i = 0; i < divs.length; i++){

    divs[i].style.display = "none"
    }
}

This function is selection any and all <div class="whatever"> elements. Guess what's sitting in your HTML code, wrapping around the entire graph?

You guessed it...a <div class="content">. So the JavaScript is only doing what we asked it to do. We need to do something here to distinguish the divs we really want to select. And we can do that because we're using querySelectorAll which lets us identify elements using CSS identifiers. CSS has a way of indicating ancestor descendant relationships that I'm going to look up now...stay tuned.

ebeshero commented 4 years ago

@amberpeddicord There's another problem I'm trying to sort out. Even with no JavaScript associated and if we comment out the CSS that shuts off display, the <div> elements inside <foreignObject> are not displaying. I discover that this is because you need to have literal number or percentage values in the @width and @height attributes on SVG <foreignObject>. I'm experimenting now to see what works...

amberpeddicord commented 4 years ago

@ebeshero Ah, I see! On the tutorial I found, it said that the default for those values was "auto", but I was wondering if that would actually work...

ebeshero commented 4 years ago

@amberpeddicord It seems that it doesn't. I can get them to display and give them different widths using either numbers or percents.

ebeshero commented 4 years ago

@amberpeddicord I'm reading this StackOverflow on the subject, and the very last piece of advice here seems possibly worth trying: https://stackoverflow.com/questions/16254651/auto-height-for-a-foreignobject-in-svg

ebeshero commented 4 years ago

@amberpeddicord Good news! I repaired the HTML, CSS, and JavaScript together so <foreignObject> can work. I introduced an additional <div> for one of the entries because it was really too long to deal with tidily in the browser window, and it's working. I also added a @viewBox to your SVG element to shrink the plot just a bit and pull the text away from the edges of the screen. I've pushed the code up to the repo: take a look! You might want to experiment with styling a little more.

One thing I notice is that you've got some SVG element properties in your CSS file, and CSS isn't happy about them (like stroke-width which isn't a CSS property). I'd put those in the SVG itself.

alnopa9 commented 4 years ago

@ebeshero @amberpeddicord Browsers don't like multiple javascript files in the <head>. It chose to run the first one and ignored the second. We hit another problem because browsers also do not like multiple functions that are window.onload. To get around this I made a function called start that holds the two other functions, initialize (your timeline function) and my sticky function, scroll. I then added an event listener (window.onload) that loads start when the page loads. This calls the other two functions and now we have one javascript file that does two things! 🎉 🌮