Data attribute as callback identifier - javascript

Having a HTML/css/Javascript based menu system.
The menus themselves are dynamically built using Javascript and ends up in something like this:
<ul>
<li><span class="icon icon-star"> </span></li>
<li><span class="icon icon-boat"> </span></li>
<li><span class="icon icon-bike"> </span></li>
</ul>
I also plan to have a serverside version (build the menus using server script) and / or static versions. Have to do some benchmarks when it is all up and running.
The span holds icons by sprite background.
Now, instead of adding event listener to each menu entry I add one to the UL and on klick or key-down I locate the selected menu entry from the event. Typically:
a = event.target;
// Find the LI element.
for (li = a; li && li.nodeName != "LI"; li = li.parentNode)
;
// Find the anchor (Typically if the span was clicked).
if (a.nodeName !== 'A')
for (; a && a.nodeName != "A"; a = a.parentNode)
;
This works fine but the question becomes how to identify which action should be taken – as in which menu entry was clicked.
Would it be OK to use the data- tag?
Typically something like:
<a href="#" data-menu="1">...
And then use a switch routine to determine menu entry.
My initial thought was to use ID's, but as I see it using the data- attribute is more flexible and less prone for clashes and complications.
Initially the idea behind attaching only one event listener per menu is to lessen the load at page load and instead do the finding when menu entry are clicked as by then in most cases that can take precedence when it comes to resource usage. I also need to style the LI element by adding/removing a .menu_selected class upon menu selection.
I'm a bit unsure about the design as a whole. It works fine by the (few) test I have done, but there might be some caveats I'm not seeing.
This is targeting never browsers only. HTML5 + canvas.
… and now I'm unsure how good a question this is, but I'll give it a try and delete if not appropriate.

Would it be OK to use the data- tag?
Certainly, you can delegate events on any selector.
The jQuery library makes this much easier too:
$('ul').on('click', '[data-menu]', function () {
...do awesome stuff...
});
The code above binds a click handler to any and all <ul> elements, and fires off event callbacks on any element clicked within the <ul> elements that matches the '[data-menu]' selection.
I don't recommend adding jQuery for the sake of adding jQuery, but jQuery is very useful for managing delegated events. If you're able to take the minor performance hit of downloading an additional library, then it'll make writing code significantly easier.

Related

Switch <li> active element

Here is a link for a fiddle project I am working on right now. What I am trying to do is to switch active menu element depending on what section is displayed right now on screen. So if it is Kontakti on screen, then Kontakti in menu (<!--NAV BAR-->) has to display as active item. I am not familiar with jS
Add data-role=navigate attribute to ul element where navigation is housed,
In the javascript section of this fiddle,
please try with the following code,
$(function()
{
$("[data-role=navigate]").find("li > a").click(function()
{
$(this).parents("ul").find("li.active").removeClass("active");
$(this).parent().addClass("active");
})
})
I will explain in brief what the code does...
1) Binds a click event handler to <a> inside <li> which is inside <ul> with attribute data-role=navigate
2) When the click happens, it removes the active class for the current element.
3) Assigns the active class to the immediate parent of the <a>
It is a good practice to target specific needs in JS by placing attribute in the DOM elements and hooking up event listeners using that attribute.
Hope it helps!
Bootstrap's Affix might be something that could be useful in this case. It highlights what part of the page is displayed on the screen on a separate sub-navigation part of the page.
Btw, if you have Bootstrap code you can display it on Bootply quite easily. It provides Bootstrap's CSS and JavaScript files by default.
You say you're not familiar with JavaScript but you're asking for functionality that needs JavaScript. I'd recommend trying to use a plugin if it's not something you can write yourself.
Waypoints would do exactly what you're looking for:
http://imakewebthings.com/waypoints/guides/getting-started/

Is it an ok practice to change an ID using javascript if the element is unique?

I have an unordered list, where I am using an ID to identify the current list item that was last clicked. On click of a different item, I am using js to switch the id to a different item in the list, so that styling could be applied.
It seems intuitive to me that a unique item that can only be used once (a selected li item) should be identified as an ID, and that is why I did that, but I was told it is bad practice to do so.
I wouldn't do so. This rather seems an opportunity for you to discover HTML 5's data attribute, which you can use for both CSS styling (using attribute selector) and JS use (with dataset or jQuery's .data() method).
Quick definition from MDN :
HTML5 is designed with extensibility in mind for data that should be
associated with a particular element but need not have any defined
meaning. data-* attributes allow us to store extra information on
standard, semantic HTML elements without other hacks such as
classList, non-standard attributes, extra properties on DOM, or
setUserData.
In HTML side :
<!-- You can use data without value, and just test its presence -->
<li data-selected>
...
</li>
In CSS :
li[data-selected]{
...
}
Your question actually has a couple of interesting components, let me try to answer them as good as I can :-)
"Is it an ok practice to change an ID using javascript if the element is unique?"
You could say it's a justifiable case, if the element really is unique, then there's no real harm in "moving" the ID with JavaScript.
BUT
In your description you touch on something more fundamental:
"On click of a different item, I am using js to switch the id to a different item in the list, so that styling could be applied."
If this is the case (you change the ID for styling), then I'd recommend using a class instead. People previously already gave you a good hint, something like an "is-active" class would be very useful as it's less specfic than an ID, can be used on multiple items if needed and if you use classes that determine a state (like "is-active", "has-children", "is-animating", etc.), it's really easy to re-use them in later parts of code as well and it is clear what the element is doing at the moment.
A little code for reference:
HTML
<ul>
<li>Some item</li>
<li class="is-active">Some item</li>
<li>Some item</li>
<li>Some item</li>
</ul>
CSS
.is-active {
color: #eee;
background-color: #222;
}
jQuery
// You probably want a bit more specific selector, but it's just an example.
$('li').on('click', function() {
var $element = $(this),
$elements = $('li');
if (!$element.hasClass('is-active')) {
$elements.removeClass('is-active');
$element.addClass('is-active');
}
});
Since you might need to reference the specific id of an element at some future point, changing it is probably a bad idea. In your case it would be better to just apply a class to the last item clicked.

DOM manipulation performance when toggle visibility

I have bunch of images in a wrapper div. I need to hide all of them except the one user clicks on.
What is the most performance concise way of doing that?
Should I apply class on all of them to hide them all and than filter out the one that was clicked and show only that one or should I loop over them all hiding them as loop progresses with exception of the one that was clicked or something else?
Tnx
In modern desktop browsers you won't see any difference. Browsers are tuned so that they are blazing fast in rendering any changes is DOM three. Guts tell me that it might be sligtly faster to loop through all images and set visibility depending on item clicked using style attribute and not using class. In that way you have to process only N elements and no external CSS files are involved. If you hide all and show the element with was clicked, you process N + 1 elements.
In your situation I would use solution that is fastest, more managable and clean from the developers standpoint as there won't be much difference in the final result if you use one method or another.
PS: If you're using jquery, you can use the following:
Lets say, your div has id='test-div', and there are several images in it. All these images can be accessed as:
$('#test-div img')
Now, lets assume you know the id of image which got clicked. Lets assume id='my-image'.
You can execute the following to hide all other images (except 'my-image'):
$('#test-div img').not('#my-image').addClass('hide')
One of the most performant ways would be to let CSS do the visibility. It sounds to me like you're only displaying one at a time, in which case you can do it with two DOM operations by using classes;
// scope above
var lastClicked = null;
// then in click listener, 1st param `e`
if (lastClicked) lastClicked.className = ''; // remove visible class
lastClicked = e.target; // get clicked node
lastClicked.className = 'visible'; // add visible class
I'm assuming event.target but depending how the listener is attached, you might want to use this or some other logic. Further, if you expect element.classList support, you can use add and remove from that.
Example CSS of how to show only nodes with class token visible.
selector:not(.visible) {
display: none;
}

Simple Horizontal Javascript Navigation with Prototype

I'm trying to implement a simple horizontal navigation menu that just shows a single div for each link. It is kinda like a dropdown menu but instead of a mouseover triggering a dropdown, an onclick event will trigger the showing of a div. I want to make sure I am taking the right approach before going too much further, any help is appreciated. This is what I have so far:
<ul id="settings_nav">
<li>
<a>Theme</a>
<div id="settings_block"><%= render :partial => 'email_password' %></div>
</li>
<li>
Lists
<div id="settings_block"><%= render :partial => 'lists' %></div>
</li>
</ul>
window.onload = function(){
settingsMenuInit('settings_nav')
}
function settingsMenuInit(settings_nav){
$(settings_nav).childElements().each(
function(node){
node.onclick= function(){ this.next.show() };
})
}
Something like that, but I am unsure how to get the div that is currently shown and hide it. I could iterate through all the childElements and hide each div and then show the one that is being clicked, but maybe there's a better way?
Some notes FW(T)W:
With Prototype and similar libraries, you don't want to hook up event handlers by assigning functions to the element's onclick and similar properties; that style has several disadvantages (not least that there can only be one handler for the event on the element). Instead, use Prototype's observe function:
someElement.observe('click', functionRefHere);
// or
Element.observe(someElementOrID, 'click', functionRefHere);
This also lets Prototype work around some IE memory loss bugs for you.
You might look at is Prototype's dom:loaded event, which happens sooner than window.onload (which won't happen until all of your images and other external resources have loaded, which can be a second or two after the page is displayed):
document.observe('dom:loaded', initFunctionRefHere);
You can use event delegation and just watch your settings_nav element, rather than each child node individually.
$(settings_nav).observe('click', handleNavClick);
function handleNavClick(event) {
var elm = event.findElement("some CSS selector here");
if (elm) {
event.stop();
// Handle it
}
}
As you can see, Event#findElement accepts a CSS selector. It starts with the actual element that was clicked and tries to match that with the selector; if it matches, it returns the element, otherwise it goes to the parent to see if it matches; etc. So with your HTML you might look for a li (event.findElement('li')) or the link (event.findElement('a')).
But if you want to watch each one individually, they can share a function (as they do in your example):
$(settings_nav).childElements().invoke('observe', 'click', handleNavClick);
function handleNavClick(event) {
// Prototype makes `this` reference the element being observed, so
// `this` will be the `li` element in this example.
}
Whether you watch each element individually or use event delegation depends on what you're doing (and personal preference). Whenever anything is likely to change (adding and removing navigation li elements, for instance) or when there are lots of things to watch, look to event delegation -- it's much easier simpler to deal with changing sets of elements using event delegation and just watching the parent. When dealing with a stable structure of just a few things (as in your example), it may be simpler to just watch the elements individually.
Once inside your handler, you can use Element#down to find child elements (so from the li, you might use li.down('div') to find the div), or Element#next to get to the next sibling element (e.g., going from the link to the div). Either way, once you have a reference to the div, you can use Element#show and Element#hide (or Element#toggle).
I recommend using named functions instead of anonymous ones (see my example above). Named functions help your tools (debuggers, browsers showing errors, etc.) help you. Just be sure not to declare a named function and use it as an expression (e.g., don't immediately assign it to something):
// Don't do this because of browser implementation bugs:
someElement.observe('click', function elementClickHandler(event) {
// ...
});
// Do this instead:
someElement.observe('click', elementClickHandler);
function elementClickHandler(event) {
// ...
}
...because although you should be able to do that according to the spec, in reality various bugs in various browsers make it not work reliably (article).

Logic behind hiding elements

I am having a dilemma in the logic of this particular issue. Forgive me if this is quite newbie question but I'd rather have a solid bg on it.
There are a lot of examples of this all around the web where you click on an element to display another element. such case may be a menu that when you hover your mouse on it (or click on it) its get displayed. Later the element gets hidden either on mouse out, OR CLICKING ON ANY OTHER ELEMENT.. so, how is this achieved? I am sure the solution is not to bind a "hideElem" function on all the elements.
regards,
I haven't done it in a while, but an easy solution is to add a click event to the top of the DOM tree that will close the open element. Here's an example in psuedo-javascript:
document.body.onclick = function() {
element.style.display = "none";
}
If you need complex behaviors inside the "shown" element, make sure your preventing the necessary events from propagating up the DOM tree.
element.onclick = function(e) {
e.stopPropagation()
}
In general, the logic is the other way around (at least with menus) i.e. the element in question is hidden until a state-event unhides it, then hidden again as dictated. The point being that the hiding/unhiding logic is usually tied to the element itself, not everything else on the page.
As to how it's done, methods vary. There are lots of Javascript solutions, mostly along the lines of those already outlined, but menus can also be done purely with CSS - typically utilising the display: none; property, though you can also do stuff like setting/unsetting a negative margin so that the element is moved 'off and on the page'.
To use some of my own work by way of example:
Drop-down menu with Javascript
Drop-down menu with jQuery
Drop-down menu with CSS
$('#target').bind('click', function(event)
{
var $element = $('#element');
$element.show();
$(document).one('click', function()
{
$element.hide();
});
// If you don't stop the event, it will bubble up to the document
// and trigger the click event we just bound.
// This will hide the element right now just after showing it,
// we don't want that.
event.stopPropagation();
}
You have to keep in mind that a Javascript event goes up and down the whole tree when begin fired. So you can bind event listeners to any parent when you want to listen for an event on many elements.
This is called event delegation.
A cheap way to do it potentially is to bind an event handler to the "(on)blur" event of the clickable item and/or it's target. If your design allows.
That is one way to do it.
You could also write a method that traps (hooks into) all 'click' events regardless of the element, and hide your menu from there.
JQuery would make this task easier for you.
step 1- use a javascript library so you can have the code be as cross browser as possible - otherwise you have to cater to two different event models between internet explorer and gecko/webkit based browsers. JQuery, Mootools, YUI - all will handle this for you - there are more but those 3 are my favorite and are well documented.
step 2 - you prob would want to implement a clickshield for this - essentially a block-level dom element that is absolutely positioned over your entire page with a higher z-index than the rest of the page. attach a click event to that, and you can perform your logic for hiding elements on the page. The clickshield could easily have javascript code expand it to the width -height of your page post DOM rendering using the methods of any of the aforementioned javascript libraries.

Categories

Resources