Thread: Help with some website design...

  1. #1
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665

    Help with some website design...

    Hey guys,

    Alright, so let's say that we use a webpage as a means for data entry.

    The page is actually relatively simple. We really just have a table of select tags so the html looks like this :
    Code:
    <tr ...>
        <td ...>
            John Smith
        </td>
        <td ... >
            <select ...>
                 <option ...>
                    ...
                </option>
            </select>
        </td>
    </tr>
    Now, there's a lot of rows in this table and of course there's more than one option. What I want to do is implement full keyboard navigation of the webpage.

    Each row contains something useful, a column with a name in it. So for each <tr/>, there's one <td/> which contains a name (a basic string) and then another <td/> which contains the <select/> stuff.

    I want the navigation to be based off the name contained in each row and I want the user to be able to make every selection without once touching the mouse.

    So, I was thinking, let's take JavaScript and use that to focus() on the right elements. I found out about focus() last night and, like, freaked out about how amazing it is.

    I was thinking, maybe every time the user starts typing (only alphabet keyboard input) a small search box pops up in the middle of the screen and then when the user hits enter, the page is focus()'d to the proper select-tag.

    Is this bad design? I've never thought about this before so I was looking for some design ideas. What's a good interface that'll give the user the ability to navigate this page by keyboard and string?

  2. #2
    Registered User Alpo's Avatar
    Join Date
    Apr 2014
    Posts
    877
    I wouldn't show the text input just on key press, it would probably get annoying. You could always have it as an option though.

    You could attach a keydown event listener to the body, which would receive key events from the children elements through propagation (events propagate up though the DOM hierarchy, but will always have the target property equal to the focused element).

    So you could intercept these, then iterate through children elements (I would use j-query as it gives good cross browser performance), and put the focus on the nearest element with the value you are wanting. Basically every time the user hits a key, while focused on a select element within a table, the focus would go to the next element with that key, allowing them to jump around fairly quickly.
    WndProc = (2[b] || !(2[b])) ? SufferNobly : TakeArms;

  3. #3
    Registered User Alpo's Avatar
    Join Date
    Apr 2014
    Posts
    877
    Here I made a small sample of the sort of thing I'm talking about:

    Code:
            $(function () {
                "use strict";
                // A function to create a similar table to the example.
                var fillTable = (function () {
                    var names = ["John Addams", "Jane Doe", "Bobby miller", "Al Bundy"],
                        options = ["yellow", "blue", "orange", "red", "grey"],
                        select = $("<select></select>");
                    return function (height) {
                        var table = $("#table1"),
                            tr,
                            td,
                            sel,
                            i,
                            j;
                        for (i = 0; i < height; i++) {
                            tr = $("<tr></tr>").append($("<td>" + names[Math.floor(Math.random() * names.length)] + "</td>"));
                            td = $("<td></td>");
                            sel = select.clone();
                            for (j = 0; j < 5; j++) {
                                sel.append($("<option>" + options[Math.floor(Math.random() * options.length)] + "</option>"));
                            }
                            td.append(sel);
                            tr.append(td);
                            table.append(tr);
                        }
                    };
                }());
                
                $("body").bind("keydown", function (event) {
                    var key = event.key,
                        selects = $("#table1 tr td select"),
                        peoples = $("#table1 tr td:first-child"),
                        i,
                        j,
                        currentIndex = selects.index(event.target);
                    // Check if we are currently focused on a select element in the table.
                    if (currentIndex !== -1) {
                        // Loop through the people in the table, and if we find one whose name
                        // begins with the event.key value, we set the focus to the associated select.
                        for (i = 1; i < peoples.length; i++) {
                            j = (currentIndex + i) % peoples.length;
                            if (peoples.get(j).innerHTML.slice(0, 1).toLowerCase() === key.toLowerCase()) {
                                $(selects.get(j)).css({
                                    "color": "rgba(255, 0, 0, 1)", 
                                    "background-color": "rgba(0, 0, 255, 1)"});
                                $(selects.get(currentIndex)).css({
                                    "color": "rgba(0, 0, 0, 1)",
                                    "background-color": "rgba(255, 255, 255, 1)"});
                                selects.get(j).focus();
                                break;
                            }
                        }
                    }
                });
                
                fillTable(10);
            });
    It's sort of a silly example, since the only keyboard targets in the table I've made are the select elements. When you are focused on a select element and hit a letter, the event handler loops through the group of td elements, and if it finds a persons name that matches the input key, it focuses on the select element associated with that person. I've used the modulus operator to make sure the loop will restart after the end.

    I don't think a search input is a bad idea though, just not on every key press. You could have it in fixed position somewhere on top of the page, with other options. The regular tabbing behavior usually hits all the text inputs on a page, so it is reachable with the keyboard.

    Edit: Just added some css snaz to the keydown event handler to make it more obvious which select element has the focus. (When a match is found it turns the old element to background white, text black, and the new element to background blue with red text).
    Last edited by Alpo; 04-14-2015 at 11:16 PM.
    WndProc = (2[b] || !(2[b])) ? SufferNobly : TakeArms;

  4. #4
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    That's a pretty legit idea though.

    And that's namely because using the mouse a little bit isn't the problem. It's using the mouse in-between updating row entries that's the real problem. So having to manually focus() on one element of the table using the mouse is acceptable though I will also probably add a keyboard shortcut that'll bring you the focus() to the first element of the table.

    Alright, I think I'm just going to implement this then. Thanks for your input, Alpo. Your time developing in JavaScript shows.

  5. #5
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Update : I've thought about this a little bit more so hopefully I can get some feedback.

    I liked your ideas, Alpo, and they inspired me.

    Let's say we dedicate a keyboard shortcut to bringing up a search bar in the center of the screen. As the user types, all the strings in the table that contain what the user types as a substring show up below it. Sort of like an auto-complete form, is what I'm trying to go for. And then if the user hits enter or clicks on a name from the drop down list, the focus() shifts to the appropriate row and select element. This way, the user can also see nearby or perhaps relevant rows entries.

    Does any of this make sense?

  6. #6
    Registered User Alpo's Avatar
    Join Date
    Apr 2014
    Posts
    877
    Quote Originally Posted by MutantJohn View Post
    Update : I've thought about this a little bit more so hopefully I can get some feedback.

    I liked your ideas, Alpo, and they inspired me.

    Let's say we dedicate a keyboard shortcut to bringing up a search bar in the center of the screen. As the user types, all the strings in the table that contain what the user types as a substring show up below it. Sort of like an auto-complete form, is what I'm trying to go for. And then if the user hits enter or clicks on a name from the drop down list, the focus() shifts to the appropriate row and select element. This way, the user can also see nearby or perhaps relevant rows entries.

    Does any of this make sense?
    Yes, I think I see what you are saying. I tried a small implementation out, and it seems pretty decent. The main crinkle I had working it out is what criteria is used to tell if a search matches.

    In this example, I just take whatever is in the search bar, and check it against table elements using the String.prototype.indexOf method, so it might be more or less indiscriminate than you would want. Some other possible methods would be to slice the strings to the same length and perform an equality check (that could be too strict), or to make a RegExp object out of the search-bar value.

    I used just regular div elements to build the "autocomplete" list:

    Code:
    $(function () {
                "use strict";
    
                // The same function to build a table:
    
                var fillTable = (function () {
                    var names = ["John Addams", "Jane Doe", "Bobby miller", "Al Bundy"],
                        options = ["yellow", "blue", "orange", "red", "grey"],
                        select = $("<select></select>");
                    return function (height) {
                        var table = $("#table1"),
                            tr,
                            td,
                            sel,
                            i,
                            j;
                        for (i = 0; i < height; i++) {
                            tr = $("<tr></tr>").append($("<td>" + names[Math.floor(Math.random() * names.length)] + "</td>"));
                            td = $("<td></td>");
                            sel = select.clone();
                            for (j = 0; j < 5; j++) {
                                sel.append($("<option>" + options[Math.floor(Math.random() * options.length)] + "</option>"));
                            }
                            td.append(sel);
                            tr.append(td);
                            table.append(tr);
                        }
                    };
                }());
                
                // The search shortcut listener:
    
                $("body").bind("keydown", function (event) {
                    var key = event.key,
                        selects = $("#table1 tr td select"),
                        search = $("#search"),
                        currentIndex = selects.index(event.target),
                        // I just chose a random search shortcut "Ctrl + x"
                        searchShortcut = function () {
                            return (key === "x" && event.ctrlKey ? true : false);
                        };
                    // Check if we are on an element within the table, and that we've hit the shortcut
                    if (currentIndex !== -1 && searchShortcut()) {
                        // Make the search div visible:
                        search.toggle(300);
                        // Make sure any previous searches are clear:
                        search.children("#searchbar").val("");
                        search.children("#results").html("");
                    }
                });
                
                // The logic to build an autocomplete list, and to take you to the selected table element:
    
                $("#search > #searchbar").bind("keyup", function (event) {
                    event.stopPropagation();
                    // Collect some values for later use
                    // The searchbar value:
                    var currentSearch = $(this).val(),
                    // A wrapped set of all td elements with names of people:
                    people = $("#table1 tr td:first-child"),
                    // A wrapped set of select elements beside the people:
                    selects = $("#table1 tr td select"),
                    // The search div:
                    search = $("#search"),
                    // The searchbar (bound to this):
                    searchbar = $(this),
                    // The div that will contain divs containing the results:
                    results = $("#search > #results"),
                    // A variable to build matches out of:
                    newMatch,
                    // An array to collect search matches into:
                    matches = [],
                    // A callback for when user clicks a search result:
                    matchSelect = function (event) {
                        // Get the index of the clicked element within the results:
                        var index = results.children("div").index(this),
                            chosen;
                        // Check if clicked element was within the results set (may be redundant)
                        if (index !== -1) {
                            // Get the object that specifies where in our table the match is:
                            chosen = matches[index];
                            // Hide the searchbar and clear search.
                            search.toggle(300);
                            search.children("#searchbar").val("");
                            search.children("#results").html("");
                            // switch the focus to the chosen element.
                            selects.get(chosen.index).focus();
                        }
                    };
                    // We want new results every time the user presses a key:
                    results.html("");
                    // Go through the list of people, check their names against the search-bar value:
                    people.each(function (n) {
                        if (this.innerHTML.toLowerCase().indexOf(currentSearch.toLowerCase()) !== -1) {
                            // If we get here a match was found, so push back its index within the table:
                            matches.push({match: this.innerHTML, index: n});
                            // Build an element the user can click on:
                            newMatch = $("<div>" + this.innerHTML + "</div>").css("cursor", "pointer");
                            // Bind our click handler, and then append the newMatch element:
                            newMatch.bind("click", matchSelect);
                            $("#search > #results").append(newMatch);
                        }
                    });
                });
                
                fillTable(18);
            });
    For the search bar, I just went for a pretty simple "template" type of idea, where it starts out hidden, and is toggled to visible by the user hitting the shortcut, it goes back to being hidden when a selection is made:

    Code:
    <div id="search" style="display: none; border: 1px solid black;">
        <label for="searchbar">Search: </label>
        <input type="text" id="searchbar"/>
        <div id="results"></div>
    </div>
    Anyway it seems like a pretty decent idea, I don't know if you would want to use this implementation though (it only took 45 minutes or so, so there are probably much better ones lol).

    Edit:

    Also I forgot to mention. The reason I bind'ed the search-bar to "keyup" was for when the user begins deleting letters. If it is listening for "keypress", for some reason the backspace key did not trigger the event on any browser I tried it on.
    The "keydown" event isn't as useful here either, as when we've just deleted a key, we want the current search-bar value, rather than the value before the letter was deleted (if that makes sense).
    Last edited by Alpo; 04-19-2015 at 06:23 PM.
    WndProc = (2[b] || !(2[b])) ? SufferNobly : TakeArms;

  7. #7
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Angola
    Posts
    8,445
    Quote Originally Posted by MutantJohn View Post
    Let's say we dedicate a keyboard shortcut to bringing up a search bar in the center of the screen.
    Is this going to be a full web app, or part of a regular web page? Biding keyboard shortcuts in a browser on a page-basis is of extremely poor taste.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  8. #8
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Quote Originally Posted by Mario F. View Post
    Is this going to be a full web app, or part of a regular web page? Biding keyboard shortcuts in a browser on a page-basis is of extremely poor taste.
    Is it? What else should I try for similar functionality?

  9. #9
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,609
    Quote Originally Posted by MutantJohn View Post
    Is it? What else should I try for similar functionality?
    Autocomplete on focus is pretty much the same thing. JQuery for that: AutoComplete on focus show default drop down - jQuery Forum.

  10. #10
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Angola
    Posts
    8,445
    Well, you should use the usual strategies already available to you. The typical webpage UI elements; A link that opens said search box or other web page widgets you can devise and that offer expected usage patterns to your users, like a button or a tabbed interface coded in your webpage, for instance. Keyboard shortcuts don't belong in traditional html web pages.

    If however you are developing internet applications you are no longer bound to the same rules and concerns for web accessibility and usability guidelines. And in here, within most cases, you are free to code your internet app UI as you would a traditional desktop application.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  11. #11
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    If however you are developing internet applications you are no longer bound to the same rules and concerns for web accessibility and usability guidelines. And in here, within most cases, you are free to code your internet app UI as you would a traditional desktop application.
    Well, it's definitely not a regular HTML page. I want to call it an "app" but I'm not sure.

    Basically, it's a <form> with a <table> where each <tr> has a <td> that contains a <select>.

    The user hits the submit button and then the server does what it does from there. So I guess in that sense it is an "app". It runs only in one browser (Firefox [this is a restriction set in place by the company]) and will only work as a means of backend data entry over the local intranet.

    This is also the only page like this. So I'm not sure if that qualifies as an "app" or not but to me it feels like it would be a much friendlier user experience if it was an "app". I'm also doing this largely for me because I'm part of the group doing the data entry. Right now, the tables will have so many rows sometimes, it's easier to ctrl-f a name and hop around the page like that. But then I have to take my hand off the keyboard and use the mouse so there's a significant annoyance in terms of usability with that.

    I want to redesign this page to be optionally navigable entirely by keyboard as well as add some much needed style changes.

    I liked the idea of having a little search box pop up with an autocomplete list based on substring matching and then the focus() would shift based upon the user's selection from the drop-down.

    This is really one of the first things I've ever thought about designing so I want to hear what everyone thinks.

  12. #12
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,609
    Well, I thought a lot about this.

    Ultimately I don't have a problem with your design because:
    1) it's for you
    2) you're mimicking browser behavior. Ctrl-f already opens a "search the page" form on my user agent and lots of others.
    3) the line between web app and web page is so blurry.

    If I were in your shoes, I would take Mario's advice as something you could add. Add a link to open the search box, and make sure that you can tab through the form. I mean, what if you were really lazy one day, and ctrl-f is just one too many keys? Couldn't hurt

  13. #13
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Well, I'm doing it for me because I think building the tool will teach me JavaScript/jQuery but at the same time, I do hope that other people will want to use it.

    I do like the idea of a link to open the search box.

    A tabbed interace won't work because each row also contains a "Delete" button which will remove a row from the table. So when tabbing, it also highlights on the "Delete" as well. I'm wondering if there's a way to make elements be skippable but at that point, that's just unexpected behavior from a page.

    So I think not touching tab is the right choice and using a button to open up the search box would be nice. I could place it in a corner of the web page (and keep it fixed) and after the user selects a value from the, well, select element, the focus should shift back to the box so the entire process can be chained back-to-back rather quickly. After all, I'm doing in this hopes that the new UI will make people more productive, i.e. they fill out the forms faster.

  14. #14
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,609
    Are you familiar with the attribute tabindex? In forms, it forces the tab key to jump from one logical item to the next. So, you can simply number off the select boxes first. The only problem is that you will eventually tab through all the form elements.

    Something like this will start your user off on the right foot, then they tab through it:
    Code:
    <body onload="self.focus();document.getElementById(\"select to tab from first\").focus();">
    Last edited by whiteflags; 04-21-2015 at 12:35 PM.

  15. #15
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    I'd personally put a fixed search bar at the bottom, with a hotkey assigned to it, and the tabindex of the search entry box would always be 1. There'd be no "search" button, as searching would be re-done every time you modify the search string, and it'd just reassign the tabindex values for each input entry, best matches first, followed by no matches. I'd also show the number of matches on the search bar, and have a "reset" button to clear the input entry, and reset the tab order.

    This way, you start searching by pressing that hotkey, then typing in the search text, then pressing tab (and/or shift+tab) to roll through the entries.

    If the searching is based on fixed contents, I'd definitely just use a specific CSS class for them, and on onload read them all by looping over the entries document.getElementsByClassName() returns, and saving each elements textContent as a string into an array, and the corresponding reference to its target element (select or input element related to it) in another array.
    Otherwise, you'll have to do the search based on the current page contents -- a loop over document.getElementsByClassName() works, although you might need to handle form input elements (containing searched for text) separately.

    If the machines are so slow that the automatic search feels annoying, use a timeout on each search field modification, cancelling the previous one whenever the search field changes. This way you can set the automatic search to start, say, three-quarters of a second after the last change to the search field. You could even use longer timeouts for when the search field is short.

    If you want an example, I could cobble one together. Be warned: I do raw Javascript; I don't like any of the available toolkits, as I prefer my JS on top of a raw browser. No reason, I'm just wonky that way.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 12-11-2012, 12:25 AM
  2. Website Design....again
    By Sentral in forum A Brief History of Cprogramming.com
    Replies: 14
    Last Post: 10-29-2006, 05:47 AM
  3. Website design!
    By Sentral in forum A Brief History of Cprogramming.com
    Replies: 28
    Last Post: 10-07-2006, 12:38 AM
  4. website
    By the Wookie in forum Windows Programming
    Replies: 1
    Last Post: 11-07-2002, 02:25 PM