There are a lot of asked questions with almost similar titles with this question of mine, but you know I didn't find an answer.
My simple question is:
I have button, when I click on it, javascript creates modal window
<div class="aui-dialog">
html here...
<button id="closeButton">Close</button>
</div>
just after <body> tag.
I can bind click event of close button with no problem using jQuery live:
$("#closeButton").live("click", function() {
alert("asdf"); // it calls
$("body").find(".aui-dialog").remove();
});
My problem is, I cannot select that dynamically created modal window div by its classname. So that I could call jQuery .remove() method to make close action. Now I know, I must deal with dynamic elements in another way.
What way?
EDIT:
I think it's important to mention this:
I dont' create the modal window myself, I use liferay portal. It has built-in javascript framework AUI(YUI) that creates that modal window. I can just create that close button inside it in its view.
EDIT 2:
Modal window div class attribute value is: "aui-component aui-panel aui-dialog aui-widget-positioned"
Since jquery will read the current DOM-state when page loads:
jQuery( document ).ready(function( $ ) {
it will miss the elements you generate post to page load.
One simple solution is to listen for clicks on document, and filter with the class or element-type that you want to use to execute your code. That way jquery will find new elements generated under document, after page load.
$(document).on("click", '#closeButton', function(){
$(".aui-dialog").remove();
});
Create a reference when you're creating the modal window:
var modalWindow = $('<div class="aui-dialog">html here... <button id="closeButton">Close</button></div>');
// later...
modalWindow.remove();
To your edit:
Get the window via jQuery's parent when the button is inside the modal window:
$('#closeButton').on('click',function() {
$(this).parent().remove();
return false;
});
Many users will come on this page when they want to select some element generated runtime by JQuery and it failed, like me.
The solution is simply approach the root (the parent) of your randomly generated element and then get inner by jQuery TAG selection. For example you generate many TDs of users in a table at runtime, the element having your users list is a table with id tblUsers then you can iterate over runtime generated TRs or TDs as following:
$("#tblUsers tr").each(function(i){
alert('td' + i);
});
further if you have inputs in tds you can go deep in selection as
$("tblUsers tr td input")
Another case could be a randomly generated dialog or popup, then you have to approach its root(parent) and next same selection by TAG as stated above.
You could do a few things, but first, if you are using jQuery 1.7, better use .on(). it has replaced .live() which is deprecated.
if you have no control over the building of the modal but know that the button is a direct child of the modal, then use parent()
$('#closeButton').on('click',function() {
$(this).parent().remove();
return false;
});
if the button is somewhere deep in the parent but has a fixed depth from the parent, use parents() which gets all ancestors of the element, and then filter it to a specific depth. if the close was 2 levels deep, the index of :eq() would be 1.
$('#closeButton').on('click',function() {
//where N is zero-indexed integer, meaning first item of the set starts with 0
$(this).parents(':eq(N)').remove();
return false;
});
another way is to add the handler when the modal is created
var modal = $('modalHTML');
$('#closeButton',modal).on('click',function(){
//modal still refers to the whole modal html in this scope
modal.remove();
});
//show modal
UPDATED:
You can use:
$(".aui-dialog").live('click', function() {
$(this).remove();
return false;
});)
This attach an event handler for all elements which match the current selector, now and in the future.
Please not that this method is depreciated in newer version of jQuery and you should consider using .on() instead of .live().
I found an answer, hope it would be helpful for developers who faced with dynamically generated html with IFRAME inside.
If you have a button (#closeButton) inside that IFRAME, and you want select iframe parent window's dom elements, just add second argument window.parent.document for your selector:
// This functions is inside generated IFRAME
$("#closeButton").on("click", function() {
// body - is your main page body tag
/* Will alert all html with your dynamically
generated html with iframe and etc. */
alert($('body', window.parent.document).html());
return false;
});
Related
I'm trying to understand why loading HTML into a div block renders its class statement effectively non-existent to a click event.
My HTML code looks like this:
<div id="load-to"></div>
<div id="load-from">
<div class="load-from-css"> Hello!</div>
</div>
<button>load it!</button>
My JS code looks like this:
$('button').click(function(){
var html = $('#load-from').html();
$('#load-to').html(html);
});
$('.load-from-css').click(function(){
alert('clicked');
});
When I click the button the HTML from the lower div block is loaded into the upper div block, and then the HTML looks like this:
<div id="load-to">
<div class="load-from-css"> Hello!</div>
</div>
<div id="load-from">
<div class="load-from-css"> Hello!</div>
</div>
My question is, why does the second click event (defined in my jQuery code) only work on the original lower "Hello!" div block but not on the loaded upper one, when both have the same class definition?
Other answers have already covered the core reason for your problem (that copying the HTML of an element and placing it elsewhere will create a brand new DOM element and does not copy any events that were bound to the original element... keeping in mind that when you add an event listener, it will only bind to any elements that exist at the time that you do so)
However, I wanted to add some other options for accomplishing what you want to do.
jQuery has a few different techniques that make this sort of thing easy:
.clone() will essentially do the same thing as you are doing now*, it will copy the HTML content and create a new DOM element. However, if you pass true (ie: .clone(true)), it will clone it with all data and events intact.
* note that to truly get the same result as using .html(), you need to do .children().clone(), otherwise you'll get both the inner and outer div.. this may or may not be necessary depending on the use case
ex: https://jsfiddle.net/Lx0973gc/1/
Additionally, if you were in this same situation but did not want to make a clone, and simply wanted to move an element from one place to another, there is another method called .detach() which will remove the element from the DOM, but keep all data and events, allowing you to re-insert it later, in the same state.
ex: https://jsfiddle.net/Lx0973gc/2/ (not the best example because you won't see it move anywhere, but it's doing it!)
As another alternative, you can use delegated event binding, which actually binds the event to a different element (a parent) which you know won't change, but still allows you to target a child element within it:
$('body').on({
'click': function() {
alert('clicked');
}
}, '.load-from-css');
ex: https://jsfiddle.net/Lx0973gc/4/
The $('.load-from-css') finds all elements currently existing and .click(...) attaches a listener to all these elements. This is executed once.
Then you copy the raw html which does not transfer any listeners. The DOM has nodes onto which the listeners are attached but when you copy the plain HTML you essentially create new nodes based on the html.
Because you are copying just the HTML. The js file is loaded at the beginning, when there is just one instance of a div with the "load-from-css" class. You should execute again the code adding the listener after you copy the html. Somethinglike:
$('button').click(function(){
var html = $('#load-from').html();
$('#load-to').html(html);
$('.load-from-css').click(function(){
alert('clicked');
});
});
#load-to inner HTML is initially empty. so added click listener only for #load-from .load-from-css. Dynamically bind element will not attach the click listener.
jQuery new version have the feature to attach the event for dynamic elements also. Try this
$('button').click(function(){
var html = $('#load-from').html();
$('#load-to').html(html);
});
$(document).on('click', '.load-from-css', function(){
alert('clicked');
});
Also we can use like this
$( document ).delegate( "load-from-css", "click", function() {
alert( "Clicked!" ); // jQuery 1.4.3+
});
Simply because the page did not refresh. You loaded a content to another content without loading the page, and the browser wont recognized any event added to the loaded element.
What you should do is load your javascript tag with the load along with the content.
Your code should be like this:
<div id="load-to">
<div class="load-from-css"> Hello!</div>
</div>
<div id="load-from">
<div class="load-from-css"> Hello!</div>
<script>$('button').click(function(){
var html = $('#load-from').html();
$('#load-to').html(html);
});
$('.load-from-css').click(function(){
alert('clicked');
});</script>
</div>
In my example here:
Example
JS
$('button').on('click', showHide);
$('.overlay').on('click', showHide);
function showHide(){
$('.scroll-container').toggleClass('show');
$('.content-container').toggleClass('no-scroll');
$('.overlay').toggleClass('opacity');
}
you have a basic body with text. A clickable element (in this case a 'button') causes a scrollable container to appear and 'hover' over the original body, which can be hidden again by clicking outside of this container.
I'm not very good at JavaScript and with this example I was helped by a friend. The thing I'm struggling with now is that I need multiple different clickable elements, displaying a similar scrolling container, but with different content.
I'm doing this for a portfolio website, so imagine a bunch of photos on a page, which when clicked result in a body hovering over the original content, further elaborating the clicked project.
Do I create multiple id's for each project, together with multiple scrolling container id's, and just copy the JavaScript a couple of times?
I hope this makes sense and I hope someone is capable of explaining to me how I'm able to create the proposed effect.
First of all, you have to make a connection between buttons and containers that should be opened. One way is to use their indexes, so that when first button is clicked, first container would open. You can use this reference of the clicked object inside your function, in order to get its index. Like this:
$(this).index()
Then, you have to select all the elements with scroll_container class $('.scroll-container') and reduce the set of matched elements to the one by passing index of the clicked element to .eq() method .eq($(this).index()). Finally, you have to add show class to it addClass('show').
And because the logic is changed, you have to separate actions done on button and .overlay click events. They do not make a reverse action now, so they are not "togglers" anymore.
http://codepen.io/anon/pen/LpWwJL
$('button').on('click', show);
$('.overlay').on('click', hide);
function show(){
$('.scroll-container').eq($(this).index()).addClass('show');
$('.content-container').addClass('no-scroll');
$('.overlay').addClass('opacity');
}
function hide() {
$('.scroll-container').removeClass('show');
$('.content-container').removeClass('no-scroll');
$('.overlay').removeClass('opacity');
}
UPDATE
One thing you should keep in mind regarding $(this).index() method.
As it is written here:
If no argument is passed to the .index() method, the return value is an integer indicating the position of the first element within the jQuery object relative to its sibling elements.
That means that trigger elements should have common parent in order to maintain our logic.
In cases like this: https://stackoverflow.com/posts/32946956/edit, elements that are triggering scroll_container appearance, have different parent nodes (they are placed in 3 different divs). So, if we will call index() method for each of them, it will return '0' because they are the first and the only elements in their parent nodes.
Actually it means that you have to get the order of their parent elements, not theirs own. This can be achieved by using parent() method before index():
$(this).parent().index()
Here is updated codepen.
If I were you, I would implement a generic function to display a different content using the same function based in the button. So for that we will need something to relational the click with the content for that we can set a value in out button:
<button data-id="1">Click me 1!</button>
<button data-id="2">Click me 2!</button>
so out when we click the button we should get the value to send it to our function:
$('button').on('click', function(){
var dataButtonValue = $(this).data('id');
});
Then we can match it with the content using for example data-content-id
<div class="content" data-content-id="1">your wording</div>
<div class="content" data-content-id="2">your wording</div>
With all that we can manage what content we want to show depends on the click.
function showHide(id){
$('.content[data-content-id="' + id + '"]').toggleClass('show');
}
DEMO
I hope it's helps.
so i implemented a bit of jQuery that basically toggles content via a slider that was activated by an <a> tag. now thinking about it id rather have the DIV thats holding the link be the link its self.
the jQuery that i am using is sitting in my head looks like this:
<script type="text/javascript">
function slideonlyone(thechosenone) {
$('.systems_detail').each(function(index) {
if ($(this).attr("id") == thechosenone) {
$(this).slideDown(200);
}
else {
$(this).slideUp(600);
}
});
}
</script>
i was using this as a index type box so there are several products when you click on the <a> tag that used to be an image* it would render a bit of content beneath it describing the products details:
<div class="system_box">
<h2>BEE Scorecard database</h2>
<p>________________</p>
</div>
the products details are wrapped in this div.
<div class="systems_detail" id="sms_box">
</div>
so when you click on what used to be a image* it would run the slideonlyone('div_id_name') function. the function above then first closes all the other divs with the class name 'system details' and then opens/slides the div with the id that was passed into the slideonlyone function. that way you can toggle products details and not have them all showing at once.
note i only kept the <a> tag to show you what was in there i will be getting rid of it.
note: i had an idea of just wrapping the whole div in an <a> tag but is that good practice?
So now what i am wondering is since you need JavaScript to run onclick on a div tag how do you write it so that it still runs my slideonlyone function?
Using obtrusive JavaScript (i.e. inline code) as in your example, you can attach the click event handler to the div element with the onclick attribute like so:
<div id="some-id" class="some-class" onclick="slideonlyone('sms_box');">
...
</div>
However, the best practice is unobtrusive JavaScript which you can easily achieve by using jQuery's on() method or its shorthand click(). For example:
$(document).ready( function() {
$('.some-class').on('click', slideonlyone('sms_box'));
// OR //
$('.some-class').click(slideonlyone('sms_box'));
});
Inside your handler function (e.g. slideonlyone() in this case) you can reference the element that triggered the event (e.g. the div in this case) with the $(this) object. For example, if you need its ID, you can access it with $(this).attr('id').
EDIT
After reading your comment to #fmsf below, I see you also need to dynamically reference the target element to be toggled. As #fmsf suggests, you can add this information to the div with a data-attribute like so:
<div id="some-id" class="some-class" data-target="sms_box">
...
</div>
To access the element's data-attribute you can use the attr() method as in #fmsf's example, but the best practice is to use jQuery's data() method like so:
function slideonlyone() {
var trigger_id = $(this).attr('id'); // This would be 'some-id' in our example
var target_id = $(this).data('target'); // This would be 'sms_box'
...
}
Note how data-target is accessed with data('target'), without the data- prefix. Using data-attributes you can attach all sorts of information to an element and jQuery would automatically add them to the element's data object.
Why do you need to attach it to the HTML? Just bind the function with hover
$("div.system_box").hover(function(){ mousin },
function() { mouseout });
If you do insist to have JS references inside the html, which is usualy a bad idea you can use:
onmouseover="yourJavaScriptCode()"
after topic edit:
<div class="system_box" data-target="sms_box">
...
$("div.system_box").click(function(){ slideonlyone($(this).attr("data-target")); });
You can bind the mouseenter and mouseleave events and jQuery will emulate those where they are not native.
$("div.system_box").on('mouseenter', function(){
//enter
})
.on('mouseleave', function(){
//leave
});
fiddle
note: do not use hover as that is deprecated
There's several things you can improve upon here. To start, there's no reason to use an <a> (anchor) tag since you don't have a link.
Every element can be bound to click and hover events... divs, spans, labels, inputs, etc.
I can't really identify what it is you're trying to do, though. You're mixing the goal with your own implementation and, from what I've seen so far, you're not really sure how to do it. Could you better illustrate what it is you're trying to accomplish?
== EDIT ==
The requirements are still very vague. I've implemented a very quick version of what I'm imagining you're saying ... or something close that illustrates how you might be able to do it. Left me know if I'm on the right track.
http://jsfiddle.net/THEtheChad/j9Ump/
When the user clicks on my link, the modal element is dynamically generated. I am trying to inject some code into the modal when it is created. My problem is that JavaScript cannot target my modal as it is dynamically generated. How would I do this?
I have tried using on() but I got the error that my modal cannot be found.
$(document).on('click', '.open-modal', function() {
console.log( $('.my-modal') ); //cannot find .my-modal
}
From your comments, f you are following the Bootstrap example then you may mean to use #my-modal (or #myModal if you follow them to the T) since they are identifying the modal by ID instead of class.
EDIT:
The bootstrap modals depend on some preformatted html so I'm not sure how yours is dynamically generated.
I am trying to hide/show a class of elements in a form depending on a drop-down menu choice made by the user. See: http://jsfiddle.net/3FmHK/2/
I am new to js and have two problems, so maybe they are obvious, bear with me.
1) I am modifying by the div id, so only the first element changes (and not in this fiddle for some reason, but it does in the project). However I want all the elements of a class to modify and I haven't been able to make that work. So how do I modify the style="display" for an entire class, rather than a single element?
2) The remove does not work for newly added element, when the form is returned with values in the project, they are removable. Using firebug, the code looks identical for the GET return generated elements vs the user added elements, as far as I can tell. Why does the remove function not work for newly added elements?
I recommend using jQuery for this if you can. You can use the .on() feature to bind actions ot newly created elements and use the class selector to .hide() all classes then .show() the currently selected on by id.
It would look something like this:
jQuery(document).ready( function() {
jQuery(document).on('click', '.classname', function() {
jQuery('.' + jQuery(this).attr('class') ).hide();
jQuery(this).show();
// Or you can use the following to show a specific ID element.
//jQuery('#idtoshow').show();
)};
});
This will hide all elements with the class name. You will need to include the jQuery library before your script. Although I am only using show and hide here, you can use .remove() as long as you bind your action with .on and not just .click. You need .on to bind to newly created elements.
http://api.jquery.com/on/
Hope this helps.
Try:
$(this).parent('div').first().remove();