How do I attach a single function to a multiple element? - javascript

If I have two anchor tags that would trigger a same function, (i.e. multiple button to close a navigation bar) how would I bind both elements with the event? No JQUERY
html:
Close Nav
<ul id="nav">
<li>Nav One</li>
<li>Nav Two</li>
<li>Nav Three</li>
</ul>
Close Nav
JS:
var ctlOne = document.getElementById("ctl_one");
var ctlTwo = document.getElementById("ctl_two");
var both = ctlOne || ctlTwo //<-- this is what I tried and failed.
both.onClick = function(){
toggleNav();
}
function toggleNav(){
// either close or open nav <UL>
}
Again, I know how to do it in jQuery. I'm looking for a native javascript method.

var ctlOne = document.getElementById("ctl_one");
var ctlTwo = document.getElementById("ctl_two");
ctlOne.onClick = ctlTwo.onclick = toggleNav;

You can't do it in one line, but I don't see what's stopping you from doing this:
ctlOne.onClick = toggleNav;
ctlTwo.onClick = toggleNav;
Or, more DRY:
var both = [ctlOne, ctlTwo];
for (var i=0; i<both.length; i++) {
both[i].onClick = toggleNav;
}

REAL ONE eventHandler on multiple items
When i create lists or grids which need the same function i use only one eventhandler
and decide inside that handler what to do.
Heaving one eventhandler means if you unload the code you have to remove only one handler, but also uses alot less resources and updating the code is simpler.
In your case i had to include a long function (slice call...) to get the index of the clicked li
and check if i should execute your toggleNav function or not.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script>
(function(W){
var D;
function init(){
D=W.document;
D.getElementById('ul').addEventListener('click',h,false);
}
function h(e){
var a=e.target,i=Array.prototype.slice.call(a.parentNode.childNodes).indexOf(a);
if(a.parentNode.id=='ul'){
console.log('text: ',a.textContent,' index: ',i,' execute toggleNav: ',i<2);
}
}
W.addEventListener('load',init,false)
})(window)
</script>
</head>
<body>
<ul id="ul"><li>a</li><li>b</li><li>c</li></ul>
</body>
</html>
As you can see the code is short, no loops. But most of all you only have ONE handler!!
this naturally works on modern browsers only but with some changes it does the same in all browsers, i think something like e=e||window.event; e.target=e.target||e.srcElement..
EDIT
SIMPLE EXAMPLE WITH LONG VARIABLE NAMES and WHITESPACES everywhere as requested by matt,
but use the above example at it caches the various elements and it's compatible with external libraries even if you extend it.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script>
function inizializzation () { // Here i inizialize the eventhandler
var TheULElement=window.document.getElementById('ul'); // search
// for the element which id is ul
TheULElement.addEventListener ( 'click' , clickhandler , false )
// here i add an event handler to the element which id is ul
}
function clickhandler ( clickevent ) { // that handler i previously added
if ( clickevent.target.parentNode.id == 'ul' ) { // here i check if the
// parent node id of the element i clicked is ul
console.log( clickevent.target.textContent );
// this writes to the console
//-> rigthclick 'inspect element' (in chrome)
}
}
window.addEventListener ( 'load' , inizializzation , false )
// this is the event handler when the page loads
// which starts the first function called inizializzation
</script>
</head>
<body>
<ul id="ul">
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
<li>f</li>
<li>g</li>
<li>h</li>
<li>i</li>
</ul>
</body>
</html>
The point of this answer is not just to handle the 2 elements but multiple elements
and that reflets the titleof the question
How do I attach a single function to a multiple element?
or also multiple elements which contains multiple elements.
in that case you need to think like on a large grid
rows are the li's
inside this li's i could add buttons spans or whatever and again only with that single eventhandler control them all.
based on the row/colum number or whatever you immagination has to offer.
with cols/rows your handler is on the prantNode of the parentNode.
if ( clickevent.target.parentNode.parentNode.id == 'ul' ) {
console.log( clickevent.target.textContent );
}
and the rows/colums would be
<ul> // whatever (div span) it just has to be a element
<li> // whatever ,best if display:block/flex on all thiselements but
<whatever></whatever> // but with WHITESPACES this does not work
<whatever></whatever> // so it's better you add them with a script
<whatever></whatever> // or learn to write without WHITESPACES
<whatever></whatever> // or new lines.
<whatever></whatever> // because a textnode/newline/whitespace
</li> // in dom is a ELEMENT
<li>
<whatever></whatever>
<whatever></whatever>
<whatever></whatever>
<whatever></whatever>
<whatever></whatever>
</li>
</ul>
Are there LOOPS ? NO.
ps.: i answered this question as it has already the green flag so i just try to help but my laguage is not english so feel free to correct my text . but pls don't touch my code.

Related

What is wrong with my jQuery code?

I want to toggle a div from left to right when someone clicks on the h1 which is floating on right every thing is working just fine but I can't toggle it. Once the div appears it never goes away.
<script>
$(document).ready( function (){
/*
THIS COMMENTED SECTION IS WORKING BUT WITHOUT TOGGLE
$('h1').click(function (e){
e.preventDefault();
$('div').animate({
'left': 0
}, 'slow');
});
*/
// This is not working!
$("h1").click(function(){
$("div").toggle(function() {
$("div").animate({'left': '-32%'});
}, function() {
$("div").animate({height: '0'});
});
});
});
</script>
And the HTML:
<h1>Click here!</h1>
<div style="left: -32%; display: block;">Put whatever content you want here!
<ul>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
</ul>
</div>
The multiple function argument form of .toggle() has been deprecated in jQuery 1.8 and removed in 1.9 and it had a built in click handler. So, it didn't work the way you are trying to use it and it is no longer supported.
Here's a replacement for the multi-function version of .toggle():
jQuery.fn.cycleFunctions = function() {
var args = Array.prototype.slice.call(arguments);
// initialize index into function arguments
var indexTag = "_cycleIndex";
this.data(indexTag, 0);
this.on("click", function(e) {
var self = $(this);
var index = self.data(indexTag) % args.length;
self.data(indexTag, index + 1);
return args[index].call(this, e);
});
return this;
};
Note, this already does the .click() handler for you so you don't put this inside a click handler.
So, combined with your code:
<h1>Click here!</h1>
<div class="toggle" style="left: -32%; display: block;">Put whatever content you want here!
<ul>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
</ul>
</div>
// This works with two operations that are opposites of each other
// which your two animations are not
$("h1").cycleFunctions(function(){
$(this).next(".toggle").slideUp();
}, function() {
$(this).next(".toggle").slideDown();
});
You would then have to fix your CSS to that the two actions are reversible. In order to move with the left style, the item has to have a non-default position style setting. And, once the second click happens and the item has a zero height, the item will never be visible again so this could operate two clicks and never be seen again.
Here's a working demo: http://jsfiddle.net/jfriend00/Xj65L/
Take a look at the jquery documentation: http://api.jquery.com/toggle/
.toggle( [duration ] [, complete ] )
The first parameter is is not a function.

Not able to change div display property using javascript

I am trying to change display property of my div using js but it is not showing any change.
In Js code if statements are not becoming true though in css all the properties are set properly.
Please help I am a novice and not understanding the issue. Below is the code I am trying
My HTML Code:
<!DOCTYPE html>
<html>
<head>
<script src="main.js" type="text/JavaScript"></script>
</head>
<body>
<nav id="nav">
<ul class="main_nav">
<li id="About_me">About Me</li>
<li id="home1">Home</li>
</ul>
</nav>
<div id="About">
<p>Hello</p>
</div>
<div id="home">
<p>hi</p>
</div>
</body>
My JS code:
function About_Me_Sel()
{
var Id;
Id =document.getElementById("About");
if(Id.style.display == "block")
{
Id.style.display = "none";
}
}
function Home_Sel()
{
var Id;
Id= document.getElementById("home");
if(Id.style.display == "none")
{Id.style.display = "block";
}
else
alert("hello");
}
That won't work the first time around. The style property is not connected to your stylesheet, so JavaScript doesn't know what rule's you've set there. The style property reflects a style attribute that gets written into your element's tag. (You can hard-code one into your HTML if you want.) So the value is always null until you set it.
Before
<div id="About">
After
<div id="About" style="display: block">
Try reversing the conditional
if (Id.style.display != "block")
{
Id.style.display = "block";
}
else
{
Id.style.display = "none";
}
I used Ajax to load in other content into my div, and i got the content from other php files. this way
function changecontent(name){
var htmlUrl = name;
$.ajax({
url: htmlUrl,
dataType: 'html',
success: function(data) {
console.log("Current page is: " + htmlUrl);
$("#maincontent").fadeOut(function() {
$(this).html(data).fadeIn("fast");
});
}
});
}
HTML:
<!DOCTYPE html>
<html>
<head>
<script src="main.js" type="text/JavaScript"></script>
</head>
<body>
<nav id="nav">
<ul class="main_nav">
<li id="About_me">About Me</li>
<li id="home">Home</li>
</ul>
</nav>
<div id="maincontent">
<p>Hello</p>
</div>
<div id="home">
<p>hi</p>
</div>
</body>
eks: about_me.php
<a>I'awesome</a>
You have a number of errors in your code; I'll try to go through them one-by-one to clarify those problems and offer solutions that should, hopefully, lead to better understanding (and practice) in future.
First, an id "...assigns a name to an element. This name must be unique in a document."1. Note the 'must,' which means that a document with a duplicated id (more than one element sharing the same id value makes the document invalid, and causes problems with JavaScript, since it will only ever look for one element with a given id, and recover from invalid documents with unpredictable results).
That being the case I've amended the ids of the div elements by, effectively, adding the Content string, and amended the ids of the li elements to be single word and lower-case so that they can be predictably made to reference each other. This gives the following HTML:
<nav id="nav">
<ul class="main_nav">
<li id="about">About Me</li>
<li id="home">Home</li>
</ul>
</nav>
<div id="aboutContent">
<p>Hello</p>
</div>
<div id="homeContent">
<p>hi</p>
</div>
JS Fiddle (this still doesn't work as you'd intend, because the other problems still exist; it's merely to show the corrected/amended HTML).
Now, the JavaScript.
The reason it can't work, as noted by #Jeffman in his answer is because element.style references only the in-line styles of an element (those set with the style attribute), not the styles set with either a stylesheet or in the head of the document. This means you're comparing an undefined variable with a string, which will always be false.
You're also using two functions to do the same thing, which is wasteful and unnecessary. This is the second reason I've modified the ids of the various elements. A modified (single) function to do what you want to do:
function sel(e, self) {
// e is the click event,
// self is the 'this' DOM node
var id = self.parentNode.id,
toggle = document.getElementById(id + 'Content'),
display = toggle.style.display;
toggle.style.display = display == 'block' ? 'none' : 'block';
}
The above requires the following HTML for the a elements:
About Me
JS Fiddle demo.
Now, this still requires obtrusive JavaScript (using in-line event-handling within the HTML itself, which requires updates every time you may want to add further event-handling or change the function to call upon those events). Therefore I'd suggest moving to a more unobtrusive version, such as:
function sel(e, self) {
// e is the click event,
// self is the 'this' DOM node
var id = self.parentNode.id,
toggle = document.getElementById(id + 'Content'),
display = toggle.style.display;
toggle.style.display = display == 'block' ? 'none' : 'block';
}
var links = document.getElementById('nav').getElementsByTagName('a');
for (var i = 0, len = links.length; i < len; i++) {
links[i].onclick = function (e){
sel(e, this);
};
}
JS Fiddle demo.
Now, while this works, this still requires assigning event-handlers to multiple elements (despite being more easily done using a loop within JavaScript itself).
The easier way is to delegate the event-handling to the parent element, and assess, in the function itself, where the event originated. Which would give the following JavaScript:
function sel(e) {
// e is the click event,
// self is the 'this' DOM node
var self = e.target,
id = self.parentNode.id,
toggle = document.getElementById(id + 'Content'),
display = toggle.style.display;
toggle.style.display = display == 'block' ? 'none' : 'block';
}
var nav = document.getElementById('nav');
nav.addEventListener('click', sel);
JS Fiddle demo.
Notes:
http://www.w3.org/TR/html401/struct/global.html#h-7.5.2.
References:
addEventListener().
document.getElementById().
getElementsByTagName().
Node.parentNode.

Is there a better way to map menu click events to it's corresponding code

Okay, basically when I click a menu option a corresponding html is loaded into the content block of the layout page after executing some other set of codes, Now I have mapped each menu options' click event to it's corresponding set of codes to execute as follows;
<li onclick="changeContentTo('home');">About Us</li>
<li onclick="changeContentTo('rules');">Rules</li>
The mapping is done using switch case
case "rules":
/* something here */
$c.load("actsAndRules.htm" ) ;
break;
case "home":
/* something else here*/
$c.load("content.htm" ) ;
break;
I was wondering is there any better/proper method to do this?
I don't want to bind click event to a selector say $('.myLink').click() for example and no HTML5 coding, event handling will be the same as above
Basically what I want is to strip that mapping as much as possible out of the code and place it in a seperate obj ( which maybe resides in a seperate JS file ) something that will imitate a table/reference for mapping i.e. if I were to change a mapping I will only have to update that make-believe table.
Define a dictionary?
var $map = {'rules': 'actsAndRules.html', 'home': 'content.html'};
Now, instead of switch you can directly
$c.load($map[$arg]);
Assuming $arg is your function argument name.
EDIT
To run code only if any of the switch cases were satisfied, you can do something like this:
var $flag = false;
// your switch
...
case default:
$flag = true;
break;
...
// after switch
if (!$flag) {
// common code.
}
Try it like,
HTML
<li onclick="changeContentTo('home','content.htm');">About Us</li>
<li onclick="changeContentTo('rules','actsAndRules.htm');">Rules</li>
<script>
function changeContentTo(type,page){
$elesToHide=something;// let $elesToHide and $c are pre defined
if(!$elesToHide.is(":visible"))
$elesToHide.slideDown()
$c.load(page) ;
}
</script>
Try
<li class="nav-item" data-location="content.htm">About Us</li>
<li class="nav-item" data-location="actsAndRules.htm">Rules</li>
Then
$(function(){
$('.nav-item').click(function(){
if(!$elesToHide.is(":visible")){
$elesToHide.slideDown();
}
$c.load($this.data('location') ) ;
})
})
if(!$elesToHide.is(":visible"))
$elesToHide.slideDown();
this part seems to always be the same.
Just put it before your switch part.
Then, it only boils down to distinguish the url. You could just pass that directly:
<li onclick="changeContentTo('content.htm');">About Us</li>
<li onclick="changeContentTo('actsAndRules.htm');">Rules</li>
$c.load(parameterOfFunction ) ;
<li clickable load-link="content.htm">About Us</li>
<li clickable load-link="actsAndRules.htm">Rules</li>
$(function(){
$('clickable').click(function(e){
e.preventDefault();
$c.load($this.data('location') ) ;
});
});
<li id="l1" class="menuItem" data-item="home.htm" ></li>
<li id="l2" class="menuItem" data-item="actsAndRules.htm" ></li>
<li id="l3" class="menuItem" data-item="content.htm" ></li>
$(document).ready(function(){
$('.menuItem').click(function(e){
$c.load($(this).attr('data-item'));
});
});
Edit:
The HTML5 "data-" attribute will not be a problem even if you are not using HTML5. In ASP.Net the attributes are passed as-is to the client. Just leave the value blank where you don't need processing.
If you don't want to bind click on li, then you can bind it on the parent using event-delegation:
<ul id="list">
<li.....
<li.....
</ul>
$(document).ready(function(){
$('#list').click(function(e){
var $elem = $(e.target);
var content = $elem.attr('data-item');
if ($.trim(content)) {
$c.load(content);
}
});
});
Binding click to a selector is preferable over specifying onclick inline with each element.
You can easily wrap that snippet into a function and store it separately in external js file to be called from here.

JQuery: combine click / trigger elements for more efficiently written code?

I am trying to figure out a more efficient way to write my code which works but it seems inefficient. Essentially I have a series of Id elements in a nav that trigger a click function on various ids elsewhere on the page. I tried combining my elements but that does not seem to work:
$("#red, #green, #blue").bind("click", (function () {
$("#section-red, #section-green, #section-blue").trigger("click");
alert("#section-red (Red heading) has been triggered!");
alert("#section-green (Green heading) has been triggered!");
alert("#section-blue (Blue heading) has been triggered!");
}));
... but this just seems to trigger everything.
I can do this below but for lots of ids, it will be a monster to maintain and update. Not sure if there is a better way.
$("#red").bind("click", (function () {
$("#section-red").trigger("click");
alert("#section-red (Red heading) has been triggered!");
}));
$("#green").bind("click", (function () {
$("#section-green").trigger("click");
alert("#section-green (Green heading) has been triggered!");
}));
// etc...
I have a fiddle here that I have been playing with but still no joy. Essentially a click on the top nav trigger a simulated click on an H2 heading which works but it's just the code efficiency at this point.
I would add data attributes to your nav elements like:
<ul>
<li id="red" data-trigger-id="#section-red">Section Red</li>
<li id="green" data-trigger-id="#section-green">Section Green</li>
<li id="blue" data-trigger-id="#section-blue">Section Blue</li>
</ul>
then in jQuery:
$("#red, #green, #blue").bind("click", (function () {
var triggerID = $(this).data("trigger-id");
$(triggerID).trigger("click");
}
Using event delegation you only need to register two event handlers.
$("ul").delegate("li", "click", function() {
var id = $(this).attr("id");
$("#section-"+id).trigger("click");
});
$(document).delegate("h2", "click", function() {
console.log($(this).attr("id"));
});
EDIT
You could make it more efficient by caching the element lookups
var h2 = [];
h2['red'] = $("#section-red");
h2['blue'] = $("#section-blue");
h2['green'] = $("#section-green");
Inside the ul delegate click handler
h2[id].trigger('click');
Fiddle
First, I would create a class for the ul - in this example, I called it "sections":
<ul class="sections">
<li id="red">Section Red</li>
<li id="green">Section Green</li>
<li id="blue">Section Blue</li>
</ul>
Next, bind a click even to $('.sections>li'), get the index, and apply it to the relative div.
$(".sections>li").click(function () {
var index=$(this).index();
$('.faqfield-question.accordion').eq(index).click();
});
That's all there is to it!
DEMO:
http://jsfiddle.net/6aT64/43/
Hope this helps and let me know if you have any questions!
How about doing something like this. This works for all browsers(including IE ;-) )
document.onclick = function(event){
event = event || window.event; //IE does not pass Object of event.
var target = event.target || event.srcElement;
switch(target.id){
case "header-red":
case "red-section":
redClicked();
break;
case "header-green":
case "green-section":
greenClicked();
break;
}
};

How Do I addEventListener for Elements Coming from the Server?

How do I add a click event using addEventListener (window.onload)
when the tags in question are being generated from
the server (via an xmphttp request, no less)?
Thanks!
You can try to handle events for parent elements, which are available as DOM loaded, then get element related to event.
<ul id="list">
<li id="first">The first</li>
<li id="second">The second</li>
<li id="third">The third</li>
</ul>
document.getElementById('list').onclick(function(e){
o = e.originalTarget;
// if you click on second li o will bi the same as document.getElementById('first')
// even if li with id "first" is inserted to DOM after creating this event handler to "list"
// so here you can perform actions with it
// hope it will help
});
Thanks all.
I solved this by adding the below code to the "on success" event of the XMLHTTP request that populated the DOM with the elements coming from the server. That worked for me.
Josh, you got my head moving in the right direction (although it would have been nice to see a code illustration) so I marked your response as the answer.
if ((xmlhttp.readyState == 4) && (xmlhttp.status == 200)) {
var m_sel=document.getElementById("fcat");
if (m_sel) {
var maxi = m_sel.options.length;
for( var i = 0; i < maxi; i++ )
{
var option = m_sel.options[i];
option.addEventListener( "click", toggleElem, true );
}
}
}
You have to apply the event hanlders after the elements have been inserted into the DOM

Categories

Resources