In my HTML, I have a div:
<div id='layerList'>
</div>
In my JavaScript, I dynamically append several items to this div, for example:
$('#layerList').append("<a class='lays dropdown-item' href='#'> This is an example </a>");
So far, everything works fine. My menu is built dynamically.
When I click on the different menu items, I want to change the active state and do other things, but it's not working — the menu items don't change state. Below is the click event handler:
$("#layerList").on("click", '.lays', function () {
console.log("test") // this works, I can see it
$(".lays").removeClass("active"); // this does not work
$(this).addClass("active"); // this does not work
// other stuff, doesn't work
});
Note that this code works just fine if I add the menu items in my HTML (i.e., if I do not create them dynamically).
What am I doing wrong?
There are two ways of achieving the same effect.jQuery:You can achieve the same effect using the shorthand .click method on the jQuery prototype. Attaching the event listener to the document and filtering through children is slow and will keep getting slower as your page size increases. Let me show you the code I have used.
$(".lays").click(function(e) {
$(".lays")
.removeClass("active");
$(this)
.addClass("active");
// other stuff
return false;
});
.lays.active {
color: red;
}
<div id='layerList'>
<a class='lays dropdown-item' href='#'> This is an example </a><br><a class='lays dropdown-item' href='#'> This is an example </a>
</div>
Using the buit-in javascript DOM API:
If you want a faster solution that does not need external libs, then accessing the DOM API might be better for you. The syntax is longer than jQuery's and doesn't have method chaining
so you'll end up with a bit more code than jQuery.
[].slice.call(document.getElementsByClassName("lays")).forEach(function(element) {
element.addEventListener("click", function(event) {
[].slice.call(document.getElementsByClassName("lays")).forEach(function(element) {
element.classList.remove("active");
});
element.classList.add("active");
//other stuff
return false;
});
});
.lays.active {
color: red;
}
<div id='layerList'>
<a class='lays dropdown-item' href='#'> This is an example </a><br><a class='lays dropdown-item' href='#'> This is an example </a>
</div>
Using [].slice.call turns an iterable object into an Array.
If you wanted to add support for older browsers, then I recommend adding a polyfill for the Array.from method.
Anyway, the code does work, so I'm not sure what's happening on your computer. Have you tried using Incognito Mode or the Guest account on Chrome, or Safe Mode on Firefox? Check all your extensions, as it is working fine in my browser (Google Chrome) right now. Good luck to you! :)
try a this way $("#layerList").on("click", 'a.lays', function () {
$("#layerList").on("click", 'a.lays', function () {
$(".lays").removeClass("active");
$(this).addClass("active");
// other stuff
});
Related
I've been struggling with Javascript, of which I'm very unfamiliar.
I'd like to highlight the div after scrolling. I keep finding references to this highlight example, but anything I try doesn't do anything.
I think the problem is the example is showing how to do it in a simple html file with everything self-contained. I'm working within a PHP ecommerce platform (prestashop) and have to put the JS and CSS in their respective places. I'm not understanding how to call what's in that example correctly. Since I don't get any errors, I don't know how to troubleshoot. No errors, it just doesn't do anything.
In my HTML I have
<div>
<a onclick="test('myID')">test highlight</a>
<div id="myID">Here's the div</div>
</div>
In the JS I have
function test(myId){
$( document ).click(function() {
$( myId ).toggle( "highlight" );
});
}
Well, I fixed your problem buddy ;-)
This is the html
<div>
<a>test highlight</a>
<div id="myID">Here's the div</div>
</div>
the jQuery code
function test(myId) {
$("#" + myId).toggle("highlight");
}
$("a").click(function() {
test("myID");
});
What I did is remove your onclick from the anchor element and binded the click event with jQuery instead. And that did the trick ;-)
Here's a fiddle if you want to see it in action.
EDIT
The reason my highlighting wasn't functioning as you wished for, was because I was using an older version of the UI library. And the update also contains a way to use classes to bind click events. The fiddle above will show you.
Or if you what it more link the Highlight example you linked
you could do it like this:
EDIT
Look at this, it will maybe help you:
Fiddle
This is the HTML:
<div>
<p>Click to toggle</p>
<p>highlight</p>
<p>on these</p>
<p>paragraphs</p>
</div>
This is the Java script:
$('p').toggle(
function () {
$(this).css('background-color', 'yellow');
},
function () {
$(this).css('background-color', 'white')
});
But it is highly dependent on which version of jQuery you end up using.
This example uses 1.8.3
I have this following jquery on my view:
$(document).ready(function() {
$(function() {
$('#link1').click(function() {
$('#link2').show();
$('#link1').hide();
$('#frame').attr('src', 'http://google.com/');
});
});
});
$(document).ready(function() {
$(function() {
$('#link2').click(function() {
$('#link1').show();
$('#link2').hide();
$('#frame').attr('src', 'http://yahoo.com/');
});
});
});
On pageload, the link2 is set to hide. What the jQuery does is: when the link with id link1 is clicked, it will show the link with idlink2 and hide itself. And vice versa.
My problem is it seems that my jQuery code can still be simplified. Is there other ways I can do what I wanted with simpler version? Thanks for the help!
Working example : http://jsfiddle.net/cuJBm/
$(document).ready(function() {
$(function() {
var linkSet = $('#link1').add('#link2')
linkSet.click(function() {
linkSet.toggle();
});
});
});
The add method allows you to add a different selector to the set of matchers, thus binding both clicks simultaneously. By saving the constructed set to a variable (linkSet), it stops you from having to traverse the DOM twice.
The only two assumption made here, are
1) That in the initial state only one is visible.
2) That the id structure is meaningful, useful, and classes will not suffice.
http://jsfiddle.net/cuJBm/1/
To answer your second question about setting an attribute on #frame. There are numerous ways of doing this. Perhaps the simplest is to add the following to your .click handler (after the toggle).
if ($(this).attr('id')=='link1'){
$('#frame').attr('src', 'www.google.com');
} else if ($(this).attr('id')=='link2'){
$('#frame').attr('src', 'www.yahoo.com');
}
Personally, I would probably add a custom attribute to your link elements, something like:
<a id='link1' iframe-source='www.google.com'>
<a id='link2' iframe-source='www.yahoo.com'>
And then: (again, just after the toggle):
source = $(this).attr('iframe-source');
$('#frame').attr(src, source);
The reason for saving source if is that if you attempt to get $(this) within the .attr on $('frame'), it will (as always) return the currently matched element, ie $('#frame').
Alternately (and very similiarly to the above approach), you could use the innerHTML of the link. For example:
<a id='link1'>link1<span style="display:none">www.google.com</span></a>
<a id='link2'>link2<span style="display:none">www.yahoo.com</span></a>
And then: (again, just after the toggle):
source = $(this).find('span').text();
$('#frame').attr(src, source);
Personally, I dislike this last method as it pollutes the DOM structure, leading to slightly more expensive rendering times, and (in my opinion) less readable code. Practically, all three methods work just fine.
<p class="link" style="display:none;" data-link="http://google.com/">sfdf</p>
<p class="link" data-link="http://yahoo.com/">ee</p>
$('.link').click(function() {
$('.link').toggle();
$('#frame').text($(this).data("link"));
});
jsfiddle :http://jsfiddle.net/xqDus/1/
Use jQuery toggle()
just add this
Google
Yahoo
target is id of the frame
$(function() {
$('#link1, #link2').click(function() {
$('#link1, #link2').toggle();
});
});
I've created a video gallery which loads a Youtube video via swfobject, based on the element's data attributes. It functions quite well in all browsers, except IE. The strange behavior I am seeing doesn't make sense.
The markup for each thumbnail is as follows:
<li>
<div class="movie-image">
<a class="" data-videotitle="Title" data-videoid="$node.contribution('video')" href="http://www.youtube.com/watch?v=41ZskpgQqZ4">
<img class="video-link" data-videotitle="Title" data-videoid="41ZskpgQqZ4" src="http://img.youtube.com/vi/41ZskpgQqZ4/default.jpg" alt="Title">
<h5>Title</h5>
</a>
</div>
</li>
With this javascript binding the event:
$('.video-link').click(function(){
player.setVideo(this.getAttribute("data-videoid"), true);
player.setTitle(this.getAttribute("data-videotitle"));
window.event.returnValue = false; //IESUX
if(window.event.stopPropagation) window.event.stopPropagation();
window.event.cancelBubble = true;
//Yes, there's a lot of redundancy here. None has worked.
return false;
})
Now, here's the weird part: When I click a link in any browser except IE, the event works fine.
However, in IE, if I click on the h5 element, everything works fine. If I click on the image, however, the browser navigates to the thumbnail. Which is really odd, since that's not even the target of the link.
EDIT: I forgot to mention, I am stuck using JQuery 1.4.2 on this particular webpage.
Edit 2: Well... crap. I just tried putting together a fiddle to show the problem, but the fiddle is working fine, even with the old version of JQuery.
jQuery wraps the event object and gives it methods that do what you're trying to do only better:
$(".video-link").click(function (e) {
player.setVideo(this.getAttribute("data-videoid"), true);
player.setTitle(this.getAttribute("data-videotitle"));
e.stopPropagation();
e.preventDefault();
});
Note that return false is explicitly left out -- you don't need it.
My guess is that IE is either throwing some sort of error that stops the JS execution and ends up following the link, or the default action is not properly being stopped.
Note: This question was marked as solved once, but it figured out that upgrading to the latest jQuery was fixed only one issue. Please see the updated question below for the remaining issue.
Hi all,
I have just run into a weird issue with jQuery.Tipsy.
Here's a simplified demo fiddle: http://jsfiddle.net/6nWtx/7/
As you can see, the lastly added a.tipsy2 element does not get tipsyfied. The .tipsy2 elements are being tipsyfied within a jQuery.each() function and at this point I have the problem. Without the each() it works. Unfortunately, I need .each() to iterate through the elements to do some other stuff before I call tipsy().
Any suggestion?
Here's the source code of Tipsy: https://github.com/jaz303/tipsy/blob/master/src/javascripts/jquery.tipsy.js
IMHO the problem is using the combination of jQuery.each() and Tipsy option live:true
Update:
The other stuff I want to do before calling .tipsy() is checking for some optional configuration.
For example: Help"
In this example I will add the following option to Tipsy: delayIn:1000 If there is no delayed class associated to the element this parameter will be delayIn:0.
Using the same logic, I want to specify the following classes as well: show-top, show-left, show-right, show-bottom for the Tipsy option called gravity.
Example: Help"
The full code:
$(".tipsyfy").each(function () {
var a = "s",
b = 0;
if ($(this).hasClass("show-left")) a = "w";
else if ($(this).hasClass("show-down")) a = "n";
else if ($(this).hasClass("show-right")) a = "e";
if ($(this).hasClass("delayed") && $(this).attr("data-delayIn") != null) b = $(this).attr("data-delayIn");
$(this).tipsy({
gravity: a,
fade: true,
live: true,
delayIn: b
})
})
And here is a full jsFiddle demo with all the stuffs I want to do: http://jsfiddle.net/xmLBG/1/
If you use jQuery 1.7.1 instead of 1.6.4 it will work. Maybe that live feature is relying on something buggy with the older versions, or some not-yet-implemented feature.
Update: from what I understood, you want the tipsy plugin to be called to every element with the .tipsyfy class, present now or added in the future. You don't want to (or can't) call it explicitly before insertion. You're trying to accomplish that using the live option of the plugin. Is that right?
If that's the case I can offer a workaround. I tried to use on (since jQuery's live is deprecated) to bind some code to the load event, but it didn't work, so I bound it to mouseenter and checked whether or not the plugin was already built for that element. If not, it builds it and re-triggers the event.
$(document).on("mouseenter", ".tipsyfy", function(e) {
if ( !$(this).data("tipsy") ) {
e.preventDefault();
var a = "s",
b = 0;
if ($(this).hasClass("show-left")) a = "e";
else if ($(this).hasClass("show-down")) a = "n";
else if ($(this).hasClass("show-right")) a = "w";
if ($(this).hasClass("delayed") && $(this).attr("data-delayIn") != null) b = $(this).attr("data-delayIn");
$(this).tipsy({
gravity: a,
fade: true,
live: true,
delayIn: b
}).trigger("mouseenter");
return false;
}
});
Live example at jsFiddle.
For a small optimization, if the sole purpose of the .tispsyfy class is to instruct the plugin creation, and you don't need it afterwards, you can remove it prior to re-triggering the mouseenter. This way the checking code won't be called over and over again:
$(this).tipsy({...}).removeClass("tipsyfy").trigger("mouseenter");
As far as I can see, you don't need to iterate the nodelist. It looks like tipsy does that for you (see this jsfiddle, where in the first list every element gets its own tooltip (1,2,3).
KooiInc is right,
<a class="tipsy1" href="#" title="Tipsy">TipsyLink</a>
<a class="tipsy1" href="#" title="Tipsy">TipsyLink</a>
<a class="tipsy1" href="#" title="Tipsy">TipsyLink</a>
<br />
<div id="container"></div>
<input id="add" type="button" value="ok">
And
$(".tipsy1").tipsy({live:true,fade:true});
$(".tipsy2").tipsy({live:true});
$("#add").click(function() {
$("#container").append('<a class="tipsy2" href="#" title="Tipsy">TipsyLink</a>');
});
That will work fine
My guess is that Tipsy are uses some kind of direct mapping to the result, not using the live (in 1.6) or on in newer versions of jQuery.
So when your trying to apply the plugin to the links with the class tipsy2 it cant find any (cause your adding it to the DOM at a later stage in your code). The easiest fix to this is just to run the tipsy function at a later stage, perhaps on document.ready.
// this works
$(".tipsy1").tipsy({live:true,fade:true});
// add new tipsy element (ok)
$(document.body).append('<a class="tipsy1" href="#" title="TipsyAjax">AjaxTipsy1</a><br/>');
// add new tipsy element (not ok)
$(document.body).append('<a class="tipsy2" href="#" title="Tipsy">TipsyLink</a>');
$(document).ready(function () {
$(".tipsy2").each(function(){
// I'm doing some other logic here before I call .tipsy()
$(this).tipsy({live:true,fade:true});
})
});
(http://jsfiddle.net/8dg6S/7/)
Can't you do this instead? It is what you are asking.
$(".tipsy1,.tipsy2").tipsy({live:true,fade:true});
$(".tipsy2").each(function() {
//do your stuff
});
I have JS code that uses lowpro (prototype extension) to reorder a set of dynamically generated questions. So when I click a.move-up I want to move that element's parent up. And a.move-down suppose to move it down.
I'm using lowpro since the elements are generated after dom:loaded.
http://www.danwebb.net/2006/9/3/low-pro-unobtrusive-scripting-for-prototype
JS code:
document.observe('dom:loaded', function() {
Event.addBehavior( {
'a.move-up:click': function(event) {
alert('Moving up!')
//moveUp(this);
//event.stop();
},
'a.move-down:click': function(event) {
alert('Moving down!')
//moveDown(this);
//event.stop();
}
});
});
I have two links for each element (div.question) that allow that element to be moved up or down. However these click events don't get processed.
<div id="questions">
<div class="question">Q1 stuff
<a href="#" class="move-up" />Up</a>
<a href="#" class="move-down"/>Down</a>
</div>
<div class="question">Q2 stuff
<a href="#" class="move-up" />Up</a>
<a href="#" class="move-down"/>Down</a>
</div>
</div>
As part of debugging I've cut down the code to bare minimum, just trying to make sure event handling works. I don't even see the alert pop-up when I click the JS-backed links. So the "click" event isn't being handled properly.
What am I doing wrong???
Thank you!
After doing some reading/research I realized that elements that are generated dynamically...
wait for it...
are not registered/bound after DOM loads, thus the addBehavior needs to be reload()-ed for it to pick up new elements.
So after dynamically generating new elements, there has to be a call to
Event.addBehavior.reload();
After that call, new dynamically-generated elements can be moved up/down just like I want.
I knew it had to be something as simple and obvious as that... sigh