How can I know when the dom node I care about in a template is rendered. I would like to be able to select it with jQuery or template.find and either modify it or attach callbacks? I've been using Template.templateName.rendered, but it's hit or miss. Sometimes the element I want has not been inserted in the dom even though the template is rendered. If I setTimeout within template.rendered for a 2-3 seconds and then try to select an element it is almost always there, but this obviously a kludge.
Template.templateName.rendered = ->
# This is a kludge!
wait 2000, =>
$(window).resize =>
# Neither of these selectors find anything without the wait
$iframe = $(this.find('#embed-html iframe'))
width = $('#embed-html').innerWidth()
resizeIframe($iframe, width)
$(window).resize()
The rendered event is the correct way of doing this and will be fired each time the template is inserted into the DOM. Have you tried doing this without the iframe? It's possible the iframe is messing things up, in which case you could try testing the iframe with a local document source to see if loading speeds of the iframe are affecting how the find method returns elements.
You definitely shouldn't have to wait for anything before talking to the DOM, so something weird is happening.
Related
How can i go to a window position after an update on my Repl example? When i scroll down and click on a card and go back to the main window it allways jumps to the top. I have tried to insert window.scrollTo on afterupdate and also beforeupdate, inside the OpenCard.svelte also inside the App.svelte but i cant figure it out. What would be the best solution for this?
Repl
There is more than one solution for this, because this is quite a lot of code for such a question.
one solution would be to add this line to your showHome function
function showHome() {
visible = true;
requestAnimationFrame(() => window.scrollTo(0,100)); // That's the line
}
HOW IT WORKS:
The reason this solution works is because you condition the entire app on the visible variable, so after setting visible = true; the app hasn't updated yet, and therefore calling window.scrollTo immediately will just scroll before the refresh.
thus putting it inside a requestAnimationFrame will wait for the next redraw of the DOM, making the scroll only after the DOM's update.
Another solution would still require requestAnimationFrame,
There's no avoiding it using.
It will require to create a reactive statement like this:
$: if (visible) requestAnimationFrame(() => window.scrollTo(0,100));
That way your app will always know when to change the scroll based on your visible variable's value.
I think you should go with the second solution, it's more stable on future changes.
To learn more about requestAnimationFrame and how to use it: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
Debugging an app & I stumbled upon something I never noticed before. For a quick example, I've got a simple link with 2 helpers to style it, like this:
<a class="{{tabHasError}} {{activeTab}}">Test</a>
The helpers that go into this are as follows:
tabHasError: function() {
console.log('invalidated!');
}
activeTab: function() {
if (Session.equals('activeTab', this.tabIdx)) return 'active';
}
Now, every time the Session var changes, activeTab gets invalidated, which is expected. What's not expected is that tabHasError is also invalidated. Why does this happen? Is this normal? Is it because they're both attached to the same element? Aside from merging the functions, any way to avoid this? Or even better, why did MDG make this design decision?
With iron-router, it's normal to observe the behavior you're describing.
The current template in use will be refresh as soon as there is a change into the main computation dependencies. Calling Session.set will call the refresh of the template variable. For sure, it's a lot, but it is one of the simplest way to be sure the template is always up-to-date.
If you're looking for larger app, you could have a look on React.js integration, which will give you the ability to refresh only the good variable on your template.
In fact, in your example, the value of tabHasError should not change, but the re-rendering of the template will called the function tabHasError to check if there is any change. In this case, no.
I'm around if the behavior isn't clear enough. Have a tremendous Sunday!
I noticed that this only happens in an element's attributes. I think this behaviour is very specify, according to Event Minded videos regarding the previous UI engine (Shark): it only rerenders affected DOM elements.
Having in consideration that in your code Blaze is rerendering the DOM element, it makes sense to invalidate previous computations related to it. If you place this helper inside the a element it won't be invalidated.
I'm using meteor & IronRouter, and trying to do some javascript-driven animations when the template is ready/rendered.
However, the IR hook events don't seem to fire when they should.
onBeforeAction and onAfterAction only seem to fire before the new page is rendered.
So there is no updated DOM to animate.
I also tried overriding the action, calling render myself, and then animating.
however, the DOM is still not updated... even using Meteor.defer
action: ->
this.render() # works ok
Meteor.defer ->
Template.SceneView.animateScene()
This was still getting called while the old template was present. I guess since Meteor is still updating async-ly, and so defer() didn't really defer...
So a hack around this is to call the animate function from the template itself, and then also use Meteor.defer to call the animation, so the DOM is updated.
The Blaze rendered() hook only fires once on template creation.
Separately there are some new ui_hooks in Blaze, but these seem to be before insert or deletes happen. I guess I could use this and take over the whole DOM manipulation but that seems like overkill for just playing some animations when a page is ready...
https://github.com/percolatestudio/transition-helper/blob/master/transition-helper.js
UPDATE. I found out about the afterFlush event
So it seems that having reactive templates, somehow makes defer timing not work.
Instead if I use the following, the DOM is all updated in time for the animation trigger.
action: ->
this.render()
Deps.afterFlush ->
Template.SceneView.animateScene()
You could also use plain css, if you have a package like animate.css in your app (http://daneden.github.io/animate.css/)
If you have this class animated bounceIn on the div or element thats at the top of your template it will also display an animation when the template comes into view.
As you can see here, I have set up an addon to replace some contents of the network-inspect-popup with pretty-printed values. The odd thing is, I'm setting background of both the iframe and the inner document (background;#FFF), but it still shows the bluish background of the net-inspector. What's wrong with this, and why does DOM Inspector addon not even let me inspect to see what's wrong here?
Your own code overrides your own style. You have:
netPanel.iframe.addEventListener('load',function(event) {
var doc = event.originalTarget;
doc.body.style.backgroundColor='rgb(85,87,128)';
// ...
}, true);
This event listener will be called for all load events bubbling in the netPanel.iframe DOM, and that includes the load event the iframe document you insert creates. Change your code to first check the load is actually coming from a document you want to "overlay".
PS: You can actually use the DOM Inspector. But note that the "window" is not actually a window, but a <panel> in under the browser.xul top-level DOM.
Using jquery, I am swapping some content in a web page by use of jquery's .load() function. I want to trigger an event immediately once the content has actually been loaded, but not before and not after. I'm up for any graceful solution! Why? In this instance, I'm doing the old "fade out, swap content, fade in" approach. My problem? I want to fade back in AS SOON AS the new content is loaded.
Caveats:
Using a callback function as in $('#object').load(url, callback) triggers as soon as .load() function successfully executes (before the content is actually loaded). Useless here.
Using a timed delay for fading back in is not a graceful solution. Very "clunky", especially for those with faster Internet connectivity.
JavaScript's onload event trigger does not work, as the element that .load() is altering has already loaded into the HTML DOM.
jquery's .ready() function also does not work, as the actual element is already loaded.
I do not want to create an onload or .ready() sub-container element, because that's a workaround for what I'm actually trying, though it might be as graceful or more.
How can I fire a function when (and only when) the new .load() content is finally loaded, just like JavaScript's onload event does? Thanks much.
EDIT As it turns out, the jquery .load() function is working flawlessly, and I'm approaching this wrong.
Once the .load() function completes successfully, it calls any "callback" function included by the programmer, just like any other jquery function that accepts a callback as one of its "arguments".
The .load() function is complete once it either errors or successfully begins the HTML replacement and loading of new content, but that is IT! The content will then take however long it takes to load, but your .load call is already complete before that. Therefore, expecting the callback to run after the .load content has loaded will only disappoint you. ;)
I hope others can learn from this just as I did, including those who thought what I thought was the case. Proof: as stated in the jquery ajax .load page, the callback is executed when the request completes, not when the load completes. Eureka. Whoops. /EDIT
Try using a different method rather than load(), I would suggesting using get(). Something like this may be more useful to you...
var jqxhr = jQuery.get(url,vars);
jqxhr.success(function(data){
# This will only be called once the remote content has been loaded in
# The data will then be stored in the data param and can be used within your site
});
jqxhr.error(function(data){
# Something went wrong, never mind lets just handle it gracefully below...
});
I hope this is a solution to your problem!
For more information see http://api.jquery.com/jQuery.get/
I have quickly created this function below that may be of help to you... its not refined!
jQuery.fn.loadNewData = function() {
var object = jQuery(this);
var jqxhr = jQuery.get(arguments[0]);
// check for success
jqxhr.success(function(data) {
// load the data in
object.html(data);
});
jqxhr.error(function(){
alert('Failed to load data');
});
}
Using this you can call how similarly to how you would call the load() function.
jQuery('#object').loadNewData(url);
I think you might be misinterpreting what you are seeing. Depending on the browser you are using you won't see the new elements in the browser if you pop up an alert in the callback because it won't rerender the DOM until you cede control back to the browser. That doesn't mean you can't grab the new elements from the DOM and start fading them in. Take the following jsfiddle: http://jsfiddle.net/ttb55/8/ in Chrome it will show the first div when the second alert is up, then fade in the second div. In IE it won't show the first div when the second alert is up, this is the state I think you are in after load during the callback, but it still works once you hit ok because everything was in the DOM as promised.
Upon reading the jQuery docs pages for jQuery.get() and jQuery.load(), the callback argument is quoted as the following:
"A callback function that is executed if the request succeeds."
Let me stress the terms "request" and "succeeds". The request may succeed, but that does not mean that the content is loaded. Same problem as .load() — the functions aren't built the way I was thinking.
If I want to trigger an event once the new content finally loads, I'll need to take a different approach.
I could use the JS onload event, and trigger it by completely replacing an HTML element (having the replaced code contain an onload property). EDIT: Note that using HTML iframe elements is pretty awful, primitive, and "clunky". I just need to find a better way to trigger a function as soon as loading the new content finishes.
Also, I could use jQuery to check the .ready() state of new content, but ("AFAIK" / as far as I know) that will only work if the checked content is a new HTML element, not a preexisting HTML element whose interior content is changed. The jQuery .ready() status of any preexisting element will (AFAIK) already be shown as "ready" despite if new content is loading. Perhaps I am wrong about this, and I would like to be corrected if so.
Unless otherwise notified, this answer will be marked as the correct one. The original question was mistaken that .load() was all I needed. Cheers!