I'm working on creating a Javascript Tab library. Actually, it's already been developed and is working great. But it's due for a rewrite to fix some underlying annoyances and quirks we've found while using it. Anyway, here's the current model.
The current model has a TabSet object, which houses the main functions for the tab library: addTab, removeTab, showTab, hideTab, and related history functions. Then there is a Tab object that contains the data/methods related to the tab: showThrobber, hideThrobber, reload, and creating the actual DOM elements for the tab. Now, you can see this is a bit disjointed. TabSet handles showing a tab and hiding a tab and Tab handles reloading the tab.
Here's the question: What is the best way to organize the methods for this tab library. The problem we are running in to is that the current model, while disjointed, makes sense. TabSet is indeed showing a tab, hiding a tab and removing a tab. But the Tab itself is indeed being shown, hidden and removed. Really, all the function make sense to be in either class: TabSet or Tab.
Let's use an analogy. When someone needs to talk, everyone needs to stop talking. There are two ways for this to happen. 1) The leader (TabSet) tells everyone to stop talking and then tells the speaker to start talking. 2) The speaker tells everyone to shutup and starts talking. It makes sense to have the controller tell other Tabs to hide and tell the new Tab to show. But it also makes sense to have the Tab tell all the other Tabs to hide and then show itself.
What are your thoughts?
Personally, I'd design it so that a Tab only knows about itself and the TabSet that is managing it. It wouldn't carry a reference to the other tabs.
I'd put show() method on Tab; internally it would ask the TabSet to hide all other tabs and show itself.
I'm assuming exactly one tab must be shown at all times, so there's no point in having a public method to hide an individual tab.
The idea behind that design is that you have less error checking to worry about. If your TabSet object had a showTab(tab) method, you'd have to check to make sure that the tab being passed in was actually one of the tabs in the set.
Usually when I design an API my first priority is to make it hard or impossible to pass in invalid inputs.
Related
I've been trying to resolve this for days!
The use-case is one that I would think to be very common; I have a button (<button id="add-item">) that adds a new item element (<div class="item">) to a container element (<div id="list">) which in turn should display a form (<form>) for the item in a panel (<div id="panel">). I want to be able to click on the items and have the panel show the form specific to the item.
The problem is that the only item recognizing a click event is the last item added; none of the previously added items views will recognize a click.
I had this working before by having a click event on the List view on .item but was told by a prominent member of the Backbone.js community that the item view really should handle a click itself rather than delegating that responsibility up to it's container.
In order to help you who might be able to help to understand the problem I have created an extremely pared down and self-contained example to illustrate. You can see it any of these three (3) places:
A JSFiddle
A Gist
Our Website
Also, if you have any suggestions for how to better structure this code I'm 100% enthusiastic to hear them; I'm new to Backbone.js and do not feel I fully understand it's patterns, practices and idioms.
P.S. I've read everything here on StackOverflow that I can find and I can find nothing that addresses the problem so in case you want to close my question as a duplicate please do me the favor and make sure it actually is a duplicate first.
UPDATE
I'm fixing the code but leaving the errant this.$el.empty() commented out so others can who have the same issue can learn from this. Thanks to #sardine/#tollmaz for helping me out on this over Twitter.
Per a Twitter conversation, it turns out that the issue is with:
this.$el.empty();
When you create the new item, the events are set up. Upon clicking the item, the select method is executed. As part of this method, an instance of app.ListView executes its render method. Within this method, $el, which is actually #list, is emptied. As such, the previously created view with working events is nuked. While it is repopulated visually, the event is never recreated (not sure why this is though).
Context: This is specifically a Windows 8 Metro application question based on their HTML/JS framework.
I want to put one or two of the most common secondary actions within the template for my items in a ListView. Think music player. Clicking the name plays the track immediately but I also want an "Add to playlist" button on each row.
When I add a button inside my template, even if I add a handler to that button, the onItemInvoked function for the ListView always gets called and my button's handler never gets called. This would be fine if I could identify from the onItemInvoked CustomEvent what element had been clicked, however the target and sourceElement are always the outer div of the template and never the actual thing that was clicked.
Does anyone have some suggestions on how I can go about making this work? I've seen the samples for selecting an element and presenting the additional options in an appbar but for something as commonly used as "Add to playlist" that's just going to be a poor user experience.
I posted this same identical question on the msdn dev forums in the hope of getting an answer ASAP. Thank you to jpsanders over there who pointed me at win-interactive:
http://social.msdn.microsoft.com/Forums/en-US/winappswithhtml5/thread/cd5e3b84-8405-4894-9f37-3608e448bae9
Before explaining the question, I want to explain my main goal (If there is a better way than my approach):
I have the document element available with me and ideally I wanted to get a browser element such that it identifies a tab uniquely. In my previous approach I used
gBrowser.getBrowserForDocument(doc);
This returned the browser which was indeed unique to the tab (in the sense that attributes stored in it persisted across pages).
If instead, I don't store the browser element, and after moving to another page in the same tab I try the above command again, then the browser is no longer the same one as before (in the sense that it has lost all the stored attributes).
Therein lies my main problem. I want to get hold of the tab browser which I am able to refer to using different documents loaded in the same tab.
I read about a similar function:
gBrowser.getBrowserForTab(tab);
I have a feeling this might work. But again, I am not able to understand where I can get the parameter "tab" from (given a document).
Note: I am using GWT for the development of the extension
EDIT: To clarify the intent of the question, here's the use case as well as my approach:
In my extension, I am interested in monitoring user behaviour on particular websites. In a way it can be thought of as a session which remains active until the user stays on the same website. During the session, I am often required to store various attributes specific to user behaviour. One of the attributes concerning the question in "isSessionActive":"Y" or "" (blank string stands for no)
To make the code more optimal, I do not instantiate a browser for all the tabs in the beginning. Instead, I wait for the cue using an onLoad function. : if a relevant website is visited
Once that happens, I make a call to get the browser using the current document element, see if it has a non empty value for the attribute isSessionActive. If it does not, I set the attributes value to "Y" and instantiate my class which handles the profiling after that.
If it has value "Y", I know that the session is still active and that I don't need to initialize.
The problem which I'm facing is that after the first instantiation, when I move to another page within the same tab, I expected that the call to
gBrowser.getBrowserforDocument(doc);
would get me the browser instantiated previously since it is basically the same tab.
This is not happening. Each time I get a new Browser instance which does not have the attribute isSessionActive as "Y" (probably because the new page has a new document element). Thus, at present all my code instantiates over and over again which is what I do not want.
If you're only working with the current tab (and not any background tabs), then you could just use gBrowser.selectedTab https://developer.mozilla.org/en/XUL/tabbrowser#p-selectedTab
I have a list of items for which I want to show a couple of items, then a "more" button. I would like the more button to show the new items in a popup box. There are many ways to make this work, but I'm trying to figure out what is the best practice.
Here is my approach. We use MooTools and Clientcide on our site:
Directly following the "more" button, I include a div that contains the content I want to put in the popup (the full list, including a duplication of those items that are visible by default), with a class that includes the style "display:none".
I attach an event to the more button that runs a script called "popupNext". popupNext takes the next element after the button (using getNext from mootools), and creates a new StickyWin (via Clientcide and stickywin.ui) with that element as its content. Then (and this is the part that feels especially hacky) it removes the class that includes the "display:none" style from the content element.
Finally, I use element.store() (from mooTools) to store the StickyWin (with the key "win") in the event element. I neglected to mention above: when popupNext runs, it first checks via element.retrieve() whether there is an existing StickyWin, and shows it, if there is.
This all seems OK, I guess--the biggest disadvantage is page bloat--while I'm showing only first couple of elements of each list, there may be more that are loaded with each page but never seen. But I'm curious whether there is some better, standard way of doing this. For example, I could reduce bloat by retrieving the elements via ajax, at the expense of slower response when a user wants to see the full list.
Check out StickyWin.Ajax - it seems to be closer to what you need than the plain StickyWin.
I'm having trouble understanding conceptually what I should do while trying to make my first large Javascript web application.
Depending on which tab a user has selected, I show different content inside a container. The content is more than just text and uses different Javascript functions and events. I am using the Yahoo! UI Library's "TabView" implementation, but the way that this issue should be handled would probably apply to other Tab approaches.
What I was thinking of doing was basically the following:
Create separate modules for each tab (e.g. MYAPP.modules.tabCalendar and MYAPP.modules.tabJournal). When the user clicks on a different tab (or navigates with browser buttons to a previous tab state), I could call MYAPP.modules[oldModule].disable() and MYAPP.modules[newModules].enable(). These functions would subscribe or unsubscribe their custom events (for example, a general click handler attached to the container).
An alternate approach to dealing with events might be to have a single global click handler. If the click is inside the container, then determine which tab is currently selected and send the click event to MYAPP.modules[currentTab].onClick().
Or, the global click handler could fire a Custom Event to which the modules have subscribed since page load, and each module's onClick events will run and determine whether or not they should do anything.
There seem to be a lot of options, but I've been having trouble finding resources that talk about the best ways to do things like this. Am I on the right path? How should I handle this?
Use the events already built into TabView to queue your JS to do things.
http://developer.yahoo.com/yui/tabview/#handlingevents
For tab changes you'll be told the previous/next tabs selected and such which should be more than enough for your JS to figure out what it should do. If you want to write a translation layer that'll look at the events and do something based on it that's fine but it's not strictly necessary.
I'm a bit fuzzy on the problem.
Yes, you should modularize your code.
Have each module setup event handlers on the elements in their respective container.
That's it. YUI TabView handles the tab switching so you don't need to enable/disable anything.