I have page specific html loading into an iFrame which contains objects that I'm trying to reference with jquery. When the page loads initially the elements are found as expected, but if I go to a new page and return or refresh the page the elements are no longer found consistently (fails more often then not) until I clear the cache and reload the page.
I've tried different combinations of querySelectorAll and getElementsByClassName but they only work consistently that first time in. It would seem like a race condition but why would it work the first time?
Does this problem sound familiar to anyone?
It did turn out to be a race condition where the dynamic elements were not yet available for reference. In my case the iFrame element was created using ExtJS so it needed this additional listener so I could take action after the load (putting this delay in afterrender came from another post that I'll need to find and give credit to);
afterrender: function (cmp) {
var element = cmp.getEl();
element.on('load', function () {
renderer.onIFrameReady();
});
}
Related
I have a function that dynamically creates div elements based upon whatever input is given, and lets them choose certain items by clicking on each div. I have it so that if the div is clicked, a function (named checkToggle) is called that makes it looks like it is selected and adjusts some related variables. There is a checkbox in the div element that is toggled by this function (hence its name). Long story short, I had to jump through some hoops to get it to work, most of which I don't even remember. Please don't ask me about that.
The point of this question is this. I initially used the following JavaScript code to run the function when the checkbox was clicked. It was assigned by the main function, which created these div elements using a for loop.
document.getElementById(`${itemID}-checkbox`).onclick = function() {
checkToggle(`${itemID}-checkbox`);
};
This works, but I wanted to try to convert all of my onClick functions to JQuery. Here is the JQuery alternative I created.
$(`${itemID}-checkbox`).on(`click`, function() {
checkToggle(`${itemID}-checkbox`);
});
While the code itself seems to be fine, it does not work. It seems as if JQuery functions cannot be created like this in a for loop or something. It is applied after the element is created and put in its place, so I don't think it has anything to do with the element not being ready. I am also having the same issue with 2 other similar cases. Any idea as of why this isn't working?
Let me know if more information is needed and if so, what kind of information is needed.
You need to update the selector to Target HTML id using the # character. Simply prepend the character to the query:
$(`#${itemID}-checkbox`).on(`click`, function() { checkToggle(`${itemID}-checkbox`); });
It would also apply to DOM methods querySelector or querySelectorAll as well.
Hopefully that helps!
So, I have done some research, and it's pretty clear that id should be unique in the DOM. This is my issue, and I am curious what the best solution to it is:
I am using jQueryUI tabs as well as a custom menu and ajax to load specific pages into a content pane without re-rendering the browser. From some of these sub pages, a user can open a popup (done with a jQueryUI dialog) to edit customer information. Because these load a server side page, in each place that this form would be generated, it uses the same ids.
I have found that there are a number of ways to close a dialog without removing it from the DOM. This causes confusion later when it, or another form is opened elsewhere, and now there are conflicting ids present in the DOM. I am working on tracking down all the ways to close a dialog, and making sure to replace them with .dailog("destroy").remove() to make sure that they are erased from the DOM, but I want to be sure the solution here is fool proof in the event that someone one gets left on the page.
My two immediate thoughts:
1.) Generate a random string to append to each form element's id when the form is rendered, fully preserving uniqueness of the id.
2.) Use more specified selectors when getting the form data, i.e. scoping it to the popup that was created, the page that it was created from, and then the tab that it is under, and not worrying as much about id uniqueness.
The first feels ugly, and in theory you COULD randomly duplicate the string and still run into an issue. The later just feels bulky and ugly to me. Is there an option I am missing? What is best practice when it comes to dealing with IDs that can be duplicated in this way?
Thanks,
Eric
You may use classes if you need "similar" objects. Id's purpose is to identify object uniquely.
By the way, classes are widely used, for example, in Bootstrap.
UPDATE: I think your "second" approach is bad, as you eventually can change the layout, but, in this way, you should track every change, and remember WHERE to change your selectors (possibly, it will be multiple places).
Before inserting the new element into the list, you could check if there is already an element existing on the page with that id. If it does exist than delete it.
Like:
if($("#"+your_id).length!==0)
$("#"+your_id).remove();
//insert the new element
But if you need that element as well, i would suggest that you use classes to group elements used for same purposes.
Here is what you can do to distinguish between the different dialogs when you try to close them:
1) Change each dialog id into a class, so that your dialogs can share the same class. Using the same id is not recommended.
2) You can create a click listener for the button that closes the correct dialog by using the event callback parameter. See the working snippet below.
var closeButtons, i, closeButtonsLen;
closeButtons = document.getElementsByClassName('close');
for (i = 0, closeButtonsLen = closeButtons.length; i < closeButtonsLen; i += 1) {
closeButtons[i].addEventListener('click', function (e) {
e.target.parentNode.setAttribute('hidden', true); // if you want to hide the dialog
});
}
<div class="dialog"><button class="close">first x</button></div>
<div class="dialog"><button class="close">second x</button></div>
<div class="dialog"><button class="close">third x</button></div>
You can replace e.target.parentNode.setAttribute('hidden', true); with whatever you need to do. e.target.parentNode gets the dialog element.
My map application page has a div dedicated to containing a widget, such as a help window or a list of search results, floating above the map. Either a single widget or nothing is shown at any given time. There's a button for each widget, and clicking it either opens that widget (replacing what was in the div) or closes it if it's the widget that is currently open.
This is one of the functions that handles these button clicks (the aforementioned div is widgets.main):
_openSearch: function() {
if (widgets._activeWidget == widgets._searchWidget){
domConstruct.empty(widgets.main);
widgets._activeWidget = null;
} else {
widgets._searchWidget.show();
widgets._activeWidget = widgets._searchWidget;
}
},
This is the function that gets called:
show: function() {
domConstruct.empty(this.rootDiv);
this.rootDiv.appendChild(this.widgetDiv);
},
widget.widgetDiv is the div element with the actual content of the widget. My intention was to generate the content once, then keep it stored in widgetDiv if the widget is closed. This works as expected in IE and Chrome, but IE (IE8 in particular) gets rid of everything inside widgetDiv when domConstruct.empty() is called.
How do I get the desired behavior in IE? Also, which behavior is the "right" one, standards-wise?
Dojo's domConstruct.empty() (see docs here) just destroys all children – it actually sets innerHTML to an empty string for non-SVG elements.
removeChild may be a little less performant but it seems to fit your use case a little better, since it returns the removed nodes. Perhaps something like the following would work:
show: function() {
// save a reference to the current children of rootDiv to use later
var oldChild = this.rootDiv.removeChild(this.rootDiv.firstChild);
this.rootDiv.appendChild(this.widgetDiv);
}
Make sure that rootDiv has exactly one child that you want to save. If it doesn't, you could wrap it in a <div>.
For each checkbox on the web page, I replace it with a slider that I borrowed from jsfiddle.net/gnQUe/170/
This is done by going through the elements when the document is loaded.
Now the problem is that when more content is loaded via ajax, the new checkboxes are not transformed.
To solve the problem, I used AjaxComplete event to go through all the elements again and replace the checkboxes with sliders.
Now the problem happens that elements that were already replaced, get two sliders. To avoid that I check if the checkbox is hidden and next element is div of class "slider-frame", then don't process the re-process the element.
But I have a lot of other such controls as well, and I am presume I am not the only one that has this problem. Is there another easy way around it?
There exists jQuery live/on( http://api.jquery.com/on/ ) event but it requires an event as an argument? whereas I would like to change the look of my controls when they are rendered.
Another example of the same problem is to extend some controls that are loaded via ajax with jQuerys autocomplete plugin.
Is there a better way to accomplish this other than changing some attributes on the element.
To summarize, on document load I would like to process every element in DOM, but when more elements are loaded via ajax then I want to change only the new elements.
I would assume that when the element's are transformed into a slider, a class is added to them. So just add a not clause.
$(".MySelector").not(".SomeClassThatSliderAddsToElement").slider({});
So in the case of your code do something like this
$('.slider-button').not(".sliderloaded").addClass("sliderloaded").toggle(function(){
$(this).addClass('on').html('YES');
$('#slider').val(true);
},function(){
$(this).removeClass('on').html('NO');
$('#slider').val(false);
});
Since you said you do not want to add anything else, how about you change the toggle function to click.
$(document).on("click", ".slider-button", function(){
var elem = $(this);
elem.toggleClass("on");
var state = elem.hasClass("on");
elem.text(state?"YES":"NO");
elem.parent().next().val(state);
});
Running fiddle: http://jsfiddle.net/d9uFs/
I have an iframe that I dynamically set the source of. Upon retrieving the content to be displayed in this iframe, I need to be able to do a few modifications to its DOM, apply a few CSS rules, and then display it.
I tried two things (Assume the id of the iframe is content)
$("#content").attr("src", urlToFetch);
$("#content").load(function() { doSomething(); });
$("#content").attr("src", urlToFetch);
$("#content").ready(function() { doSomething(); });
The first one displays the page, then applies the changes and re-renders it. The second one applies the changes, displays the changed page, but re-renders to the original page.
Any idea how I can make it to behave the way I want?
You could try using the jQuery frameReady plugin.
http://code.google.com/p/chamilo/source/browse/main/inc/lib/javascript/jquery.frameready.js?spec=svn.classic.39a2ef8dd8332b3680a10b24c922d8676c991aeb&repo=classic&r=39a2ef8dd8332b3680a10b24c922d8676c991aeb
http://huuah.com/jquery-and-iframe-manipulation/