Dynamic Button Position

For a specific project, I needed a button to be less… static.  Using javascript, css, and a little math, I was able to hack it.

Workflow Dynamic Buttons
The buttons move as the list is resized.

In order to achieve this behavior, I needed to establish several configurations:

  • Set a control ID for the button I wanted to move
  • Set a control ID for the reference element (List Select)
  • A variable container for my CSS style text
  • Maximum and minimum y-axis position boundaries for the button
  • Visibility control for the button
  • Workflow theme styles
  • Body onload javascript to place the button at the calculated location
  • EDIT: As it turns out, these specific steps require an additional configuration step.  Either 1. vertical-align: top must be set in the css merge for the main form table id “tdBorder”; or 2. the Publishing option in the workflow designer for “Center Forms On Page” must be unchecked.  I’ll have to look into how to make it work with the forms centered vertically if that’s requested, but for now I’m good (I always center horizontally and anchor vertically to the top of the window).

First step is to create a new theme for my form.  I made a copy of the no_theme.xtheme file and renamed it, then loaded it into my project.

Next, I added some custom styles to my theme.  The “styles” used in themes roughly translate into CSS classes at runtime, so I’m able to borrow those for use in my custom CSS value.  Note that the custom styles I created are simply empty style containers.  I’ll do all the styling with CSS, not in the theme styles themselves.

Style Translation
The “Theme Style” is roughly translated into a CSS class at runtime and can be predicted and used for custom styling.

The style used for this example is “.aButtonOrange1”.  The period prefix is just a personal standard I used so that my styles are placed at the top of the list and easy to get to.

I created a button with the attributes:

Button Configuration

I placed the button at position 395, 442 (the form viewport is sized to be an iframe at 525×525).

Next, I used a Merge Text component (upstream of the form) to create some CSS content to use on our form page.  The applicable style text (and content of the Merge Text component):

.gaButtonOrange1 {
text-transform: uppercase;
font-family: 'Segoe UI', 'Lucida Grande', 'Helvetica Neue', Helvetica, Verdana, Serif;
font-size: 14px;
letter-spacing: .5px;
border: none;
text-align: center;
color: #ffffff;
background-color: #FFB43F;
padding-bottom: 2px;
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
border-radius: 0;
}
.aShowElement {
visibility:visible !important;
}

Note that CSS included in this manner always needs to be wrapped in style tags.

The Merge Text component’s config:

FormCSS
The Merge Text component holding the CSS text.

Now I had to make the button work the way I wanted it to work.  First, in order to insert custom CSS into my forms, I used the IncludeHTML component.   Two, actually.  The first of which is holding any CSS style info for my form components.

IncludeHTML
IncludeHTML configuration illustration.

The second IncludeHTML component is used specifically to initially hide the button from view, so the user doesn’t see it move as the javascript resolves the calculated location.  The CSS contained:

#ButtonSaveCAB {
visibility:hidden;
}
IncludeHTML Configuration
The IncludeHTML components aren’t seen on the form if they are configured properly. Size them at 20×20 and place them someplace on the page where you won’t lose them in the designer. As these components are themable, they can be set to outline:none; so the blue outline doesn’t appear on the components in the published form.

Now to set a reference point for my button.  I want the button to sit just below the last row in a List Select component, so I’m going to give that component a Control ID of “CABMembers” to make it easy to call.

Now that all my reference points are configured, time to write the javascript to control the dynamic button placement.  In the form body events, I’m going to add an onload event and write my javascript there.

Body Custom Events Configuration
This illustrates where the javascript will live in the form.
var bodyRect = document.body.getBoundingClientRect(),
allrows= document.getElementById('CABMembers').querySelectorAll('tr');
var lastrow = allrows[allrows.length-1];
var buttonSave = document.getElementById('ButtonSaveCAB');
if (! lastrow) {
buttonSave.style.top = 295;
}
else {
var lastrowPos = lastrow.getBoundingClientRect(),
offset = lastrowPos.top + lastrow.offsetHeight - bodyRect.top + 8;
buttonSave.style.position = 'absolute';
if (offset < 446) {
buttonSave.style.top = offset;
}
else {
buttonSave.style.top = 446;
}
}
buttonSave.className += 'aShowElement';

The javascript is calculating the position of the last row of the List Select component and returning the value.  I’m padding that value by 8px to put some space between the button and the final row.  If the position of the last row is greater than 446, then consider y-axis 446 the maximum and place the button there (the list select is scrolling at this height, and I don’t want the button to continue to descend).  If less than 446, then use the calculated y-axis value of the last row and add 8px, and put the button there.  If no rows exist, then place the button at y-axis 137px.  Finally, because the #ButtonSaveCAB component id is set in our second IncludeHTML CSS text as visibility:hidden, we need to show it.  It’s been placed in the appropriate position, so now we add the class aShowElement (seen in the CSS script above) so that the visibility is set to visible.

All seems to work as I’d hoped.  There may be contingencies for which I haven’t accounted, but I imagine that if so, I’ll hear about them after the customer has had time to work with the form a bit.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s