dojo: destroy all widgets by dom node - javascript

My content is replaced with ajax but sometimes an element will have the same id on two pages (ie, a photo on the home page has the same id on the gallery page). This means that when dojo.parser.parse is called, the widgets are trying to be re-added, and the below error is thrown:
Error: Tried to register widget with id==____ but that id is already registered
Ideally, what I'd like to do is run destroyRecursive on the DOM node that the AJAX replaces. I've tried both of the below but neither work (I believe destroyRecursive is for widgets not DOM?):
dojo.byId('main').destroyRecursive();
dijit.byId('main').destroyRecursive();
Is there a good way of doing this, or do I need to try and ensure that all my id's are different?

You are on the right track, and you are correct that destroyRecursive only exists on widgets. However, there are a couple of choices to accomplish what you want to do.
If you're using widgets to a significant extent, and the div in question is regularly being used as a bucket to hold content including widgets, then I would highly recommend you have a look at dijit.layout.ContentPane. ContentPane is a widget primarily focused around the idea of a container that receives content, either directly or from a URL, which may or may not include widgets.
Right now you're probably doing something like this on each page change:
dojo.xhrGet({
url: 'something.html',
load: function(html) {
dojo.byId('main').innerHTML = html;
dojo.parser.parse(dojo.byId('main'));
}
error: function(error) { ... }
});
With a ContentPane, you could do the same thing like this:
cp.set('href', 'something.html'); //use attr instead of set if < dojo 1.5
With this, ContentPane will not only fetch that URL and hold its contents - it will also parse any widgets within it - and equally importantly, it will automatically destroy any existing widgets within itself before it replaces its content.
You can read more about it in the Dojo documentation:
http://dojotoolkit.org/reference-guide/dijit/layout/ContentPane.html
http://dojotoolkit.org/api/dijit/layout/ContentPane
Alternatively, if you don't feel like using a widget to hold your content, you can look for widgets in your div and destroy them yourself. Here's the easiest way to do it:
dojo.forEach(dijit.findWidgets(dojo.byId('main')), function(w) {
w.destroyRecursive();
});

dojo.query('selector').forEach(function(node){
dijit.byNode(node).destroyRecursive(true);
});
Basically, selecting the node... You can get the mapped as widget object by using dojo.byNode(node), and then destroyRecursive(true);

I solved a similar problem, simply deleting from registry using dijit.registry.remove('idName') after eliminating the content with destroyRecursive(false), before Reloading it.
if(typeof registry.byId("tableOfContents") != "undefined"){
registry.byId("tableOfContents").destroyRecursive(false);
dijit.registry.remove('tableOfContents');
}

If you have more than one widget to be destroyed on a page, the following solution works for me.
var widg = dijit.findWidgets(dojo.byId('root-id')); // root-id is top div id which encloses all widgets
$(widg).each(function(){
dijit.byId($(this).attr("id")).destroy(true);
});

Related

Materialize css initializing tabs for each result

I am doing a school project that is a Movie lookup app connected to guidebox API. I am using Materialize CSS and trying to organize the information into their tabs system. They are added dynamically so the documentation says to initialize in javascript. It says to use:
$(document).ready(function(){
$('ul.tabs').tabs();
});
However that doesn't work for me I guess since the tabs are not present at Doc Ready thaey are not pushed into the DOM until a submit request. I put just the
$('ul.tabs').tabs();
into a few places in my code and the best result was it working on the first movie returned on each search but for each subsequent return item the tabs break.
I could use some guidance on whether I can plug that in somewhere to make my existing code work.
https://github.com/jasonboru/group_project1_guidebox.git
There are some missing ending tags in your dynamically created dom elements.
Apart this, in this file assets/js/logic.js the following there are the following two lines:
$('.guidebox-search-results').append(movieResult);
$('ul.tabs').tabs();
That menas, whenever you add new tabs element you initialize them.
The mistake I see is: in this way you initialize every tabs not only the new one. And, because you have already initilized the old one I can suggest you to rewrite the previous two lines in this format:
$('.guidebox-search-results').append(movieResult);
$('.guidebox-search-results').find('ul.tabs').tabs();
You can bind on every event, when node inserted in dom and after that, do with it what you want.
$(document).bind('DOMNodeInserted', function(e) {
var element = e.target;
});

lightbox not working on new content

Hy, I'm having some problems making lightbox (in my case slimbox 2) work
after new content is loaded. I understand that slimbox needs to be called again, but I tried almost everything.
Here is the code how new content is been loaded:
...var link = $('<a class="loadpost" href="javascript:">Load more</a>');
link.click(loadMore);...
loadMore is the function that loads new content. this is just a piece of code. if you need the whole code let me know.
Here is the slimbox code.
jQuery(function($) {
$("a[rel^='lightbox']").slimbox({/* Put custom options here */}, null, function(el) {
return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
});
the new content has the rel attributes but it wont work. can i combine the click function from above to call slimbox code again.
Long version:
Without more of the code (if you can, make a jsfiddle or something similar) it's hard to know if this is the only issue, but one immediate issue I'm seeing is that you're using jQuery's $(" ") wrong. You're supposed to put a selector in there, so that jQuery can find whatever it is you want it to find in the DOM; what you're feeding it is a string of HTML.
Browsers use HTML to make the DOM (Document Object Model) tree, which you can think of as an abstracted logic tree of your HTML; the DOM sees 'a.loadpost' as a parent of the text node inside of it (in this case, 'Load more'). CSS and Javascript/jQuery both find information from that abstracted logic tree in pretty similar ways. With a few exceptions, if you know how to target something with CSS, you know how to target it with jQuery -- just select whatever HTML you need to select the same way you would with CSS.
So, to tell jQuery to do something to a link with a class of 'loadpost', you simply write $('a.loadpost'). $("a.loadpost") works as well.
Short version:
var link = $('a.loadmore');

jQuery Mobile "enhance" dynamically re-generated html

jQuery Mobile 1.2.0
I generate the HTML using JavaScript ($(selector).html(content)), add it to the DOM and then display it ($.mobile.changePage()).
Then I invoke an AJAX call, get some data, and re-generate the html (but the parent element, the same $(selector), stays the same, I just change its html(...)).
At this poing the HTML is not "enhanced" by jQM, no styling applied on it.
Now according to the docs I should simply call the page() function on the parent element, i.e $(selector).page().
Other places in the docs suggest triggering the create event, i.e $(selector).trigger("create").
The problem is that non of the above two methods works - the styling of jQM is not applied.
Looking at the code of jQM, I've tried triggering the pagecreate event on that element and it does work, but, this is not documented anywhere, so I'm uncertain of it, especially concerning future releases of jQM.
At some poing in the docs I've read that I can call page() on a page only once..
Anyway, is there any concise/standard way to tell jQM to "enhance" the whole element and its child-elements? Or should I simply stay with triggering the pagecreate event?
Thank you!
To recreate a whole page use this:
$(selector).trigger("pagecreate");
This was my answer to a simmilar question: https://stackoverflow.com/a/14011070/1848600. There's an example of page recreation. Take a look, this should probably solve your problem.
What is the scope of
$(selector).trigger("create");
You should be able to add any elements on the 'pagecreate' event which comes right before 'pageshow' jqm styling is applied to elements. For example I dynamically add a header/footer like this
$(document).on('pagecreate', "[data-role=page]", function() {
var header = "<div data-role='header'>some header stuff</div>";
var footer= "<div data-role='footer'>some footer stuff</div>";
$(this).prepend(header);
$(this).append(footer);
$("[data-role=header]").fixedtoolbar({tapToggle: false});
$("[data-role=footer]").fixedtoolbar({tapToggle: false});
});
Make sure you're using jquery 1.7 or above I think that's when the on method was introduced;
It sounds like you may be generating the DOM and then changing the page, try it the other way around go to the page first then dynamically edit the dom.
EDIT
set the reload page option to true
$.mobile.changePage($(page), {reloadPage: true});
Edit 2
$(selector).children().each(function(){
$(this).trigger('create');
})

Is it possible to create element on the fly with jQuery Mobile?

I have an app built using jQuery (and using various jQuery-UI tools).
For some reason, i have to port it to smartphones/tablet computer, and decided to use jQuery Mobile for that (in order to minimize the number of changes).
In my vanilla app, I created some elements of the page on the fly, depending of user interactions.
For example a slider could be created like that (p is an object with a bunch of params):
function createSlider(p){
return $("<div/>",{
"id":p.id,
"class":p.divClass,
}).slider({
"orientation": p.align,
"min":p.constraint.min,
"max":p.constraint.max,
"step":p.step,
"value":p.curVal,
"animate":"normal"
/*and some event handling here, but it doesn't matter*/
});
}
And it will produce a nice looking slider. Now it looks like:
function createSlider(p){
return $("<range/>",{
"id":p.id,
"class":p.divClass,
"min":p.constraint.min,
"max":p.constraint.max,
"step":p.step,
"value":p.curVal,
});
}
But as it's created on the fly, all the stuff done by jQuery Mobile on the page load isn't done on it.
Is there a way to force that initialization without writing the slider in the html?
Thanks.
EDIT: I found in the doc that it could be achieved using container.trigger("create");
However this does not work yet.
EDIT2: Ok create was the solution.
According to the documentation (see edit in the question), using trigger("create") on the containing element works.
And to make that work, you also need to remember that range is an input type and not a tag...
Working solution:
function createSlider(){
return $("<input/>",{
"type":"range",
"id":"sl",
"min":0,
"max":15,
"step":1,
"value":1,
});
}
function appendSlider(){
$("#yourdiv").append(createSlider()).trigger("create");
}
As a sidenote, the documentation for jQuery mobile lacks a search option.
Try calling .page() on the container the content is being added to. Alternatively, adding .page() to the content you're returning may also work.

Fetching HTML into a <div> and overwrite content

I have a website with a top panel and a body part. The top panel can be toggled between hidden and shown.
The body part should contain the actual content of the website.
As I don't want to hard-code every single site, I would like to stay with the panel and only reload the content.
This seems to can be done using AHAH and this tutorial.
The problem is that, if new content is loaded, it just pushes the old content down.
My question is, how can I overwrite the old content with the new, fetched content?
Thanks a lot in advance for the help!
Use:
document.getElementById('yourDivID').innerHTML = theHTML;
However any JavaScript in theHTML will not be evaluated.
The prototype.js library has a good function, Ajax.Updater(), to do this sort of thing, and it is less limited than the ahah.js lib, it sounds (can harvest a chunk of any page, local or remote, unlike AHAH). Like jquery, there is a special $() function to get at stuff, use that to prune before appending.
$( 'target_div_id').childElements().each( function( child) { child.remove(); } )
Ajax.Updater( 'target_div_id', 'related_page.html')
Trailing options can be added to refine how much of the downloaded "page" is used.
Ajax.Updater
Element
If you're using jQuery:
$("#divid").html("some html value goes here");
Jquery would be the best way, here is a link to that page;
http://docs.jquery.com/Attributes/html#val

Categories

Resources