Hype emits elements in DOM div order that does not match tool view

While working with turn.js, I noticed that my pages (modeled in hype element groups) were coming out in the wrong order in the client DOM (check on Chrome and Safari). My page/groups were created some by hand and others by copy/paste and drag into order. After some frustration and hacking, it seems that Hype is emitting elements in a DOM order that does not match the order the elements are shown in the Hype tool. It appears OK visually in the client because of the z-index that does reflect the order of the elements in the tool view, but the DOM order of the element divs is unpredictable and thus hazardous to any other JS frameworks that are expecting a predictable DOM order.

I believe this is a bug in Hype since the DOM output is not predictable.

I made a simple example:

Hype Tool view:

Client view (Chrome on Mac mini M1, latest Big Sur):

Elements from the client DOM:

<div class="HYPE_scene" id="hype-scene-19TURTVTF9WXUYLRG802" hype_scene_index="0" style="background-color: rgb(255, 255, 255); display: block; overflow: hidden; position: absolute; width: 600px; top: 0px; left: 0px; height: 400px; transform-origin: 50% 50%; z-index: 1; opacity: 1;"><div class="HYPE_element_container" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; pointer-events: none; z-index: 4;"><div class="HYPE_element" id="TheRedOne" hype_scene_index="0" style="pointer-events: auto; position: absolute; border-style: solid; background-color: rgb(237, 27, 19); border-width: 1px; border-color: rgb(216, 221, 228); overflow: visible; z-index: 4; width: 95px; height: 264px; transform-origin: 50% 50%; transform: translateX(176px) translateY(32px) rotateY(0deg);"></div></div><div class="HYPE_element_container" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; pointer-events: none; z-index: 1;"><div class="HYPE_element" id="TheBlueOne" hype_scene_index="0" style="pointer-events: auto; position: absolute; border-style: solid; background-color: rgb(52, 76, 237); border-width: 1px; border-color: rgb(216, 221, 228); overflow: visible; z-index: 1; width: 238px; height: 240px; transform-origin: 50% 50%; transform: translateX(105px) translateY(44px) rotateY(0deg);"></div></div><div class="HYPE_element_container" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; pointer-events: none; z-index: 2;"><div class="HYPE_element" id="TheGreenOne" hype_scene_index="0" style="pointer-events: auto; position: absolute; border-style: solid; background-color: rgb(63, 237, 20); border-width: 1px; border-color: rgb(216, 221, 228); overflow: visible; z-index: 2; width: 298px; height: 89px; transform-origin: 50% 50%; transform: translateX(75px) translateY(109px) rotateY(0deg);"></div></div></div>

The Hype runtime DOM order is based on a top-left element location. This is to better help accessibility/assistive devices which use DOM ordering to read out the ordering of elements. We intentionally made this change in Hype v3.6.

I'd be curious as to your use case for depending on a DOM ordering?

As older versions of Hype did DOM elements by z-index; we left this behavior in the app through a hidden default which you can enable by typing this into the terminal:

defaults write com.tumult.Hype4 elementInsertionOrderSortMethod "compareByZOrdering:"

To get back, use this command:

defaults delete com.tumult.Hype4 elementInsertionOrderSortMethod

(and if using the setapp version, change the bundle identifier to com.tumult.hype-setapp)

However, this does in no way guarantee that the editor will ever be the same as the output, simply that the output is ordered in a specific way.

1 Like

Blazing response time!!!!
The use case is turn.js, a jQuery plugin that produces lovely page turning book widgets (Turn.js Documentation)

image

So they depend on the DOM order for page ordering.

I did apply your hidden default with fixed my example, so I am good to go for my application.

I understand the need to use top/left ordering for screen readers.

Perhaps this should fix should be promoted to a scene level option so its more available?

Kudos on Hype....after 40 years of coding, its one of the most delightful tools I have ever used!

Regards,
Sam

Great - I'm glad that was able to solve your problem!

That is definitely a reasonable use case for having the option. While there's always hesitation at adding new and difficult to concisely explain options in Hype's UI, I've filed this in our tracker so we can see if other needs come in that might make us re-prioritize it.

Thanks for the kudos!

Nice… new addition to Hype Cookbook unlocked.

3 Likes

Just came to say: I'd really appreciate this option being exposed in the editor. My use case is a series of videos that plays in sequence. Despite putting the first video "on top" of the others in the editor, I could not understand what logic guided their rendering sequence in the DOM, so inevitably they would play out of order.

I'm glad the defaults plist option exists for now, but it'll be a pain to remember to do this on my desktop, laptops, etc, to prevent screwing up the order again.

You could query your DIVs and then sort the resulting node list based on an additional attribute, in this example data-order, before doing whatever logic you are applying. I am assuming you're doing something with code.

function sortByOrder(nodes) {
  return Array.from(nodes).sort(function(a, b) {
    return a.dataset.order - b.dataset.order;
  });
}

Lets assume your element have a class called node and the data-order attribute:

<div class="node" data-order="1"></div>
<div class="node" data-order="3"></div>
<div class="node" data-order="2"></div>

You could then do something like:

var sceneElm = document.getElementByid(hypeDocument.currentSceneId())
var nodes = sceneElm.querySelectorAll('.node');
var sorted = sortByOrder(nodes);

sorted.forEach(function(node) {
  console.log(node); // do somthing with element
});

If you don't want to use a class and only have one list, you can also query for the data attribute.

var nodes = sceneElm.querySelectorAll('[data-order]');

If they are all siblings, what about using the z-index attribute? (you'd technically need to look at each one's parent, since they are all in HYPE_element_container elements)

1 Like

Thanks for the recommendations. My development preference is always to take advantage of the IDE before writing workarounds. I'm fully aware of code-level solutions to the problem, but reducing my workload and the complexity of the project is always preferable.

As I said - I'm extremely happy that toggling a single boolean flip allowed me to do exactly what I needed in a sensible way in the editor.

1 Like