This problem has cost me a few gray hairs. Here's the short story: you can't use drupal_add_js inside hook_filter with caching enabled. If you do, the first time you view a page through the filter, everything will look dandy. Your page gets loaded as expected, you view filtered output and the javascript gets added as expected. With this page load, the content gets cached. The next time you view this page, the cached content is displayed. You see the filtered output but the javascript is nowhere to be found. It took me a couple of days and after that some good advice from Károly to see that this javascript doesn't get cached along with the content.
I can live with that, I can just put the javascript straight in the output. This way, the javascript gets cached with the output and gets displayed each time, straight from cache. Right? Maybe. This works perfectly on all browsers, but malfunctions in some cases in IE (I bet you didn't see that one coming). There's a nasty bug that forces you to place body-altering javascript right before the closing body tag. This is not possible when you put the javascript in the output, but it is possible with drupal_add_js. Sigh.
So what solution do you have? You disable caching on the hook_filter by passing the "no cache" $op. This might not be something you want to do, but it is the only workaround for this bug.
Comments
Doh!
I discovered this hacking around inside of the asset module.
Here's a question, can you use the actions module to get around this? To add a drupal_add_js call every time your hook_filter is called?
I haven't explored this yet but it's been something rolling around in my brain for a bit.
same with drupal_add_css
You have the same issue with drupal_add_css()
The solution I use in GeSHi filter for syntax highlighting is to add the needed CSS file to every page, regardless if the page contains filtered content or not.
with Drupal 5: in hook_menu(), in the $may_cache=FALSE branch
with Drupal 6: in hook_init()
I don't think this will work
I don't think this will work because hook_filter is not called when content is fetched from cache, as far as I know
great idea
That's a really great idea actually. I'll have to check that. If this works, that will make a lot of people happy :)
Nonsolution
That is not really a solution of course, since your css might break the lay-out on pages where it is not expected. This is especially the case with javascript: you don't want to enter javascript/jQuery code on pages where it is not needed or where it might break other scripts.
it is a solution when you are carefull
In case of GeSHi filter the CSS rules are very specific, making it unlikely to clash with other rules.
A better way?
hook_filter is not called when content is pulled from cache. check_markup (the function that calls hook_filter) only calls hook_filter if there is not thing in the cache for a particular id.
I'm not sure I like the idea of adding another module in the loop with more sql queries and logic.
What about this, in the case of the asset module lets use hook_nodeapi and the view op to look at the assest on the node (they were added in the previously called hook_nodeapi load op) and add the needed javascript for each type of asset attached to the node.
Just a thought. Good call on the caching.
Drupal 7
This was a huge gripe for me in Drupal 6, so we fixed it in Drupal 7 with Attached JavaScript and CSS. Using the #attached property will cache the JavaScript as part of the form, so even though the content can be cached, the JavaScript/CSS can still be added to the page. I believe hook_filter_info runs through drupal_render, so you can do the same with #attached there now too.
Yay fixing things that annoy you!
Rob, that is awesome!
Rob, that is awesome! Especially since turning off caching for that specific hook was the only solution, which is really not something I like to do. Was this solution tied to a specific issue? If it was, then could you pass the url?
Post new comment