jQuery Design Pattern for Updating Selectors When the HTML Changes - javascript

I'm looking for some advice on a making my jQuery selectors more configurable. For example, given the following HTML generated by PHP:
<ul id="my-list">
<li>first</li>
<li>second</li>
</ul>
And a bit of jQuery:
$(function() {
$(document).on("click", "#my-list li", function() {
alert("You clicked " + $(this).text());
});
});
So far so good, but then one day the designer comes along and renames the id "my-list" to "list-of-numbers", and the javascript stops working. Currently the solution is remembering to grep through the code for any references to "my-list", but that's very faulty. Is there a jQuery design pattern for making the selectors configurable?

This can be called 'selector selection.' You can define a set of variables that have the selectors in them as a solution:
var selectors = { myLi: "#my-list li",
myUl: "#my-div ul" };
Then you can simply do an object lookup to get the selector:
$(selectors.myLi);
This has the advantage of being readable and dynamic; however, in your example scenario where someone changes the ID, this would not be very helpful unless it's explicitly noted somewhere in your code that changes are necessary.
You might want to consider doing some delegation and using custom data attributes to define roles.

Define a set of selectors at the beginning of your JS-Code (don't forget to add a comment for what you use it):
var selectors = {
'listSelector':'#my-list li' /* DESC */,
'nextSelector': '.whatever'
}
and use them in your queries:
$(document).on("click", selectors.listSelector, function() {
alert("You clicked " + $(this).text());
});

Related

jQuery - Setting occurences of a class to their respective data on click

what I want to achieve is setting all occurences of a class to their designated data - except the particular occurence clicked, which is to be set to something unique. My code below gives an undefined error on the "y" variable. How would I go about contextualizing the dataset?
<div id="menu">
<ul>
<li id="menu-tos" data-info="TERMS OF SERVICE">TERMS OF SERVICE</li>
<li id="menu-contact" data-info="CONTACT">CONTACT</li>
<li id="menu-signup" data-info="SIGN UP">SIGN UP</li>
<li id="menu-login" data-info="LOG IN">LOGIN</li>
</ul>
</div>
<script>
$('#menu ul li').click(function() {
i = $(this.id);
y = dataset.info;
$('#menu ul li').not(i).html(y);
$(i).html('Something unique');
});
</script>
(to skip the explanation and see a working version, scroll to the bottom)
Your undefined error is because you trying to access dataset as if it is a variable, when it is actually a property of DOM element objects (details here).
Also, if I'm understanding your desired functionality correctly, there are a couple other issues you'll run into with your code:
i = $(this.id); this will resolve to $('menu-tos'). To select via ID, you'd need a #, like this: $('#menu-tos'). In your case, though, there's really no reason to set the current element to a variable anyway, because it is already available via $(this).
As mentioned above, y = dataset.info; won't work because dataset is a property on HTML Element objects, not a variable. Since you're using jQuery, it'd be easier to use $(this).data('info').
$('#menu ul li').not(i).html(y); this will not do what you said you're trying to do. This will set the HTML of all other <li> elements to the value of the one you just clicked. To set each one to its own value, you'll need to loop through them using .each()
$(i).html('Something unique'); assuming i was set correctly as a jQuery object, you don't need the jQuery wrapper here, you can just use i.html('Something unique');
A couple other things that would help your code:
The way you are setting your variables now (without using var), it will set them on the global scope. This may work, but it can cause collisions, and is generally avoided. Use var like var i = $(this);, and it will keep that variable only within the scope of your click() function.
When using jQuery, you should wrap your code in a $(document).ready() callback. This will ensure that the DOM is loaded before jQuery tries to bind any event handlers (such as your click handler).
After fixing the issues mentioned here, the javascript code ends up looking like:
$(document).ready(function() { // ensure document is loaded before running code
$('#menu ul li').click(function() {
// set all other elements to their own data-info attribute value
$('#menu ul li').not(this).each(function() {
var info = $(this).data('info');
$(this).html(info);
});
// set the clicked item to 'Something unique'
$(this).html('Something unique');
});
});
Checkout the working code on this JSFiddle.
Try like this:
$('#menu ul li').click(function() {
$(this).siblings().each(function() {
var info = $(this).data('info');
$(this).html(info);
});
$(this).html('Something unique');
});
Full example here https://jsfiddle.net/_jakob/q49kq3c5/1/

Creating unique IDs with JQuery

This question is prob a duplicate (but I can't find it) so I apologize if it's redundant. I am trying to use JQuery's .remove function to allow for removing (obviously) of li elements in my static HTML prototype. Rather than write out a javascript block for each li I want to remove I was wondering if it was possible to create incrementing IDs with JS alone. (I'm using Serve with HAML and SASS fyi). Here is what I have.
/view-file.html.haml
%ul
%li#removeThis1
%a.remover1{:src => "#"} Remove
/application.js
...
$(".remover1").click(function () {
$("li#removeThis1").fadeOut( function() { $(this).remove(); });
});
Any thoughts on how to increment .remover1 to .remover2, etc? Likewise li##removeThis1 to #removeThis2, etc.
I would need all this to happen on page load.
There's an ancestral relationship between the elements, so just use that to your advantage.
// don't bother numbering the class. Use a common class
$(".remover").click(function () {
// find the closest ancestor with an ID that start with "removeThis"
$(this).closest("li[id^=removeThis]")
.fadeOut( function() { $(this).remove(); });
});
Or if you don't really need an ID on the ancestor, use a class there too...
// don't bother numbering the class. Use a common class
$(".remover").click(function () {
// find the closest ancestor with the class "removeThis"
$(this).closest(".removeThis")
.fadeOut( function() { $(this).remove(); });
});
Ultimately, if you were to use IDs, you'd want to generate them on the server side, then extract the numeric portion of the ID from the element with the handler, and concatenate it into the selector for the ancestor.

Use Jquery variable to identify a div class/Id

I'm sure the answer to this is simple, I've just been able to figure it out.
I have a simple click function which applies to any links in a list being clicked. When one is clicked, I want it to remove a class on a div, which is related to one of the links attributes. E.g:
// The link
<li>example1</li>
// The div
<div id="example1" class="selected"></div>
This is the kinda thing that I've tried, but it aint right:
$("ul.switcher a").click(function(e){
e.preventDefault();
$("#" + $(this().val).removeClass("selected");
});
Any help would be much appreciated! If anyone could also tell me the JavaScript equivalent of doing this too that would be a nice bonus!
Well you're close:
$("ul.switcher a").click(function(e){
e.preventDefault();
$("#" + this.title).removeClass("selected");
});
For this sort of thing, the HTML5 "data-" attribute convention can be very handy. You'd change the markup as follows:
<li>example1</li>
Then in your code you can use the jQuery ".data()" method:
$("ul.switcher a").click(function(e){
e.preventDefault();
$("#" + $(this).data('targetId')).removeClass("selected");
});
That technique allows you to avoid "overloading" other attributes, as you've done with "title". (That's not necessarily a bad thing to do, of course, but sometimes you might want the "title" to be something meaningful, since it will after all show up when the mouse is positioned over the element.)
Change
$("#" + $(this().val).removeClass("selected");
to
$("#" + this.title).removeClass("selected");
That works because the title property of the raw DOM object reflects the "title" attribute. Details here in the draft HTML5 specification, and here in the more-established DOM2 HTML specification.
Try this,
Call the following javascript function on your link click (used jquery as well),
function removeClass()
{
if ( $('#example1').hasClass('selected') ) //Will check whether the div has the mentioned class.
{
$('#example1').removeClass('selected'); //This will remove the specified class from the div.
}
else
{
$('#example1').addClass('class1'); //This can be used to add a class to the div.
}
}
Hope this helps...

Dynamic Add/Remove Class Jquery

I am creating an autosuggest box that has a of suggestion returned to it, and I am trying to add/remove the class "searchsuggestinnerulhighlight" to individual links. Here is the dynamically returned ta
<DIV id="searchsuggestinner">
<UL id=searchsuggestinnerul>
<LI>
appalachian trail
</LI>
</UL>
</DIV>
And here is my jQuery:
$(".hoverme").live("mouseover mouseout", function(event) {
if ( event.type == "mouseover" ) {
$("#" + mocount).removeClass("searchsuggestinnerulhighlight");
mocount = $(this).attr('id');
$("#" + mocount).addClass("searchsuggestinnerulhighlight");
} else {
$("#" + mocount).removeClass("searchsuggestinnerulhighlight");
}
});
Originally I had .css("background-color"... and now I've changed it to add class and remove class and it does not work. Any ideas?
Change:
$("#" + mocount).add("searchsuggestinnerulhighlight");
To:
$("#" + mocount).addClass("searchsuggestinnerulhighlight");
mocount = $(this).attr('id');
$("#" + mocount)
That's seriously dodgy jQuery!
First, you don't need attr to get the id. You can get it with this.id. This is far, far quicker.
Second, you don't need to get the id to get a jQuery selection containing the clicked element. Just use $(this) instead.
Finally, as Gabe has said, use addClass rather than add. So, all in all:
$(this).addClass('searchsuggestinnerulhighlight');
One other thing, though -- using a ID value starting with a number was not allowed in HTML before HTML5. Its behaviour is not guaranteed.
I would put this in a comment, but I can't. Implement lonesomeday's changes, but when he uses $(this).id, try just using this.id as 'this' should already be a jquery object. (We don't want $($(this)).)

How can I use multiple Floating Help Dialogue by using 'class' instead of 'id'?

I need to use multiple floating help dialog boxes in a page. I have tried it by using 'display:block' and 'display:none' and used ID in javascript. I cannot use classes since I have multiple of them on the same page and if I use classes then all of them will be displayed/hide at the same time. However, as the number of help items are increasing in the page, I have to go back to the javascript and add more lines ...
for example:
$(document).ready(function() {
$("#help-icon1").click(function() {
$('#help-details1').css('display', 'block');
});
$("#help-icon2").click(function() {
$('#help-details2').css('display', 'block');
});
$("#help-icon3").click(function() {
$('#help-details3').css('display', 'block');
});
});
Each of them also have close icons and they should be disappeared if clicked on that close icon or clicked anywhere in the page. That means I have to write javascript functions 3 times for all the different close icons.
I tried to rely on jquery's "next" feature, but since there are many layers (div/p/span) in between the areas where the help icon is places and the help text, it becomes problamatic. Any idea or any better way to resolve this?
Thanks in advance.
I'm not quite sure I understand what you are looking for, but you can set up all the click handlers in one step, and have each one refer to itself in the handler:
jQuery(".help-icon").click(function() {
jQuery(this).css('display', 'block');
});
You can add additional class names to an element.
A div can be hidden by default, and a new class can be appended to it - to "overrule" the previous style (Hence the name Cascading Style Sheets)
<div class="hidden exception"></div>
If an element is clicked, you can append a new classname like so:
$('.target').addClass('newclass');
more info:
http://api.jquery.com/addClass/
I've not done it using JQuery but what you need is "unobtrusive javascript".
It does get done by using a class. Say you have images you all want highlighted:
<img src="pic1.png" onMouseover="this.src='hi_pic1.png';" />
so they all have the same behaviour. Give them a class:
<img src="pic1.png" class="hi" />
Then at load time, on in the script at the end of your page, yahoo-style, you write an initialisation to
- grab every element of the class
- add the event(s) you want
- set the event to use the appropriate data, e.g. by using this and by using systematic names like pic1 -> hi_pic1.
Hope this helps,
Charles
Have you tried the jQuery .each function?
EDIT: Like the following
$(".help-icon").each(function(idx, elm){
elm.click(function(){
...
})
});
If all of your help icons have the same class you can use jQuery's each function to loop through them, retrieve the associated id, replace "icon" with "detail" in the id (so #help-icon3 would become #help-detail3), and then use that to update the panel. Something like:
$(".help-icon").each(function() {
var detailsId = $(this).attr("id").replace("icon", "details");
$("#" + detailsId).css('display', 'block');
});
Let's just ASSUME that you need to use IDs for some unknown reason. Here's your answer to combine efforts:
$("#help-icon1").add("#help-icon2").add("#help-icon3").click(function() {
$(this).css('display', 'block');
});
Which equates to:
$("#help-icon1, #help-icon2, #help-icon3").click(function() {
$(this).css('display', 'block');
});
But really, you don't need to use unique IDs like this without some pretty good reasons.

Categories