I need to perform a macro after the passage has already been loaded.
I can think of a million different reasons you'd want to do this, but off the top of my head, say you wanted to get the font size of the place in which you launched the macro.
state.active.variables.fontsize = $("#"+place.id).css('font-size')
new Wikifier(place, ("The font size of this very spot is" + state.active.variables.fontsize));
If you do this inside a normal macro, it's going to execute immediately (i.e. before the passage loads) and you'll get the div properties of the last page, not the current one. I've been getting around it by wrapping the macro inside a setTimeout(function(){},0).
Doing it this way though, there's occasionally a brief millisecond of lag. It can make entire passages or paragraphs look like they flash into existence out of nowhere. Is there a better way (tiddlywiki specific) to make sure the passage content is available before executing a macro?
I continue to thank everyone here who can answer these questions, which I realize are starting to get a lot more technical.
Comments
That doesn't even take into account the fact that PassageReady doesn't even work for what you're describing. Try this in PassageReady or PassageDone: Your alert is going to appear before the passage loads. Your passage will be blank when you see it every time.
Unfortunately, you are asking for something that can only be done via terrible hacks like the one you described in your original post. But you can easily set up the macro so that it triggers the code in PassageDone from whatever passage(s) you stick it in. There are multiple ways to do it: for example, set a flag/variable in the macro that PassageDone will catch, process, and then unset.
The general idea Erik put forth is the correct way to go about this. Why don't you explain in a little more detail what you're trying to accomplish. There very well may be a way to use the general idea, but without needing to gate for every passage.
You are mistaken about PassageDone. By the time PassageDone executes, the contents of the passage have already been rendered and injected into the page. What you're seeing is that the call to alert() blocks (i.e. the browser tab in question is frozen until you close the alert modal). Since the tab is blocked, it can't update to show that your content is actually there, but it is.
I would suggest never doing that, it's buggy for two reasons:
[list type=decimal]
You're using an element reference which is not guaranteed to exist by the time the timer fires.
Any delay in the rendering, for whatever reason, after the timer is created could cause the code to fail, since it might fire before the render is complete.
And to be specific, I'm not saying don't use setTimeout(), I'm saying don't use setTimeout() in an attempt to inject content into an already rendered set of elements by using an old element reference.
Absolutely! I appreciate the help. Both of you. Let's talk through it then.
Here's a snapshot of my cursor macro being executed.
The nature of this execution means I am skeptical about the use of PassageDone for the following reasons.
1. The macro is being called more than once per passage.
2. The location of the macro in the passage changes.
3. The CSS values also change depending on where the macro is called.
Specifically what is being attempted is to get the id of the element inside the macro is being called and grab CSS information. Here is my code. And I do it again later for line-height. What does this do you might ask? Well the cursor needs to be offset depending on the font information. Here's a demonstration of what I mean.
And this code works great! Only if that delay is there though. Otherwise when it looks for the element I specify, it's looking on the wrong page. Yes, whether its done in the macro or on PassageDone. The same thing happens. It will grab it's values from the last page. And if there is no last page, it's just undefined, which then the macro just breaks entirely.
So the problem, really, is TiddlyWiki's place variable. It does not work unless the passage has been displayed. Otherwise $("#"+place.id) breaks, because there are no elements on the DOM.
Just thinking about it offhand, my approach to implementing the pointer would probably be to assign a class to the element that is being pointed to, and then use a :before CSS pseudo-element on that class to display the pointer. On its own, this might do away with the offset problem. If not, you can set offsets in percentages, or just set different offsets altogether by incorporating the containing element into your CSS rules as a (e.g. two rules with different offsets, ".big-box .pointer" vs ".little-box .pointer"). To move the arrow just toggle the class from one element to another; jquery has a number of methods to make this quick and easy. Example in case the basic setup isn't clear: http://jsfiddle.net/2ZqN6/
http://jsfiddle.net/2ZqN6/5/
(You can use the arrow keys)
Theres an issue though. You can't use % because the position property is set to absolute. You can't move the position property to relative because then you get a big gap where the hand cursor goes. Any ideas?
Eliminate the gap by moving the affected element back into place
http://jsfiddle.net/2ZqN6/7/
Use a background image and adjust offset using the background-position property
http://jsfiddle.net/2ZqN6/6/
But also, you can just set different offsets for different boxes: http://jsfiddle.net/2ZqN6/8/ (see the two CSS rules at the bottom)