How to update multiple divs with Ajax

Published on and tagged with ajax  cakephp  tip  tutorial

This is a question which arises from time to time in the CakePHP google group. There is an example in the group, but I have to admit I didn’t understood it the first time I read it. So I try to provide a better example.

First we create a view with an Ajax link and two divs we want to update:
<?php echo $ajax->link('Link', '/test/update', array('update' => array('first', 'second'))); ?> <div id="first"> first div </div> <div id="second"> second div </div>

The update function of the test controller is very simple, we just say it should use the ajax layout:
function update() {    $this->layout = 'ajax'; }

As last step we have to create the update view.
<?php echo $ajax->div('first'); ?> first div updated: <?php echo strtotime('now'); ?> <?php echo $ajax->divEnd('first'); ?> <?php echo $ajax->div('second'); ?> second div updated <?php echo $ajax->divEnd('second'); ?>

That’s it. If you click on the link the divs should get updated (I say “should” as it crashes for some reason my Firefox, but it works fine with Konqueror).

Update (2006-08-18): To avoid that Firefox crashes, you have to use a prototype version higher than 1.4. Thanks to Josh Southern for the hint!

29 comments baked

  • nate

    Side note for the readers: if you use RequestHandler in your controller, putting $this->layout = 'ajax'; in your action is not necessary.

    I’m not sure what’s up with the Firefox crashes. This was tested in Firefox 1.5.0.4 and 1.0.7 on both PC and Mac, and both with extensions enabled and disabled (I have a few common extensions installed on each, i.e. Firebug and WDE, etc).

    If anyone is able to isolate the problem to a particular version or extension, please email me or open a ticket.

  • cakebaker

    @nate: Thanks for the addition. I tested it with Firefox 1.5.0.2 and 1.5.0.4 on Debian. I also disabled the extensions, without luck.

  • nate

    Hmmm, sounds like a Debian/Linux issue : /

    I don’t have access to a Linux box, so any patch submissions on this issue will be greatly appreciated.

  • dijiko

    IMHO firefox shouldn’t crash like this with no reason…
    I think it’s more a firefox’s bug rather than a cake’s one !

  • Anonymous
  • Stefano

    I can confirm the crashes with Firefox 1.5.0.4 on Win 2k
    This my extensions list:

    Add Bookmark Here 0.5.5
    ChatZilla 0.9.74
    ClamWin Antivirus Glue for Firefox 0.2.4
    DOM Inspector 1.8.0.4
    ImgLikeOpera 0.6.9
    InfoLister 0.9c
    ListZilla 0.7
    Live HTTP Headers 0.12
    LiveLines 0.4.5
    LoremIpsum Content Generator 0.4.2
    Performancing 1.2
    RSS Editor 0.0.9.1
    SDI_Integrator 1.0
    Sage 1.3.6
    ScrapBook 1.0.6
    Signature 0.4.0.3.200511282308
    Tails Export 0.2.5
    Talkback 1.5.0.4
    del.icio.us 1.1
    mozCC 1.2.1
    undoclosetab 20051204

  • PHPDeveloper.org

    Daniel Hofstetter’s Blog: How to update multiple divs with Ajax…

  • Josh Southern

    Did anyne ever resolve the Firefox issue? It crashes on both my PC (version 1.5.0.6, Windows 2000, no extensions), and my Mac (version 1.5.0.5, OS X 10.4, no extensions). Also, the method doesn’t produce anything in Internet Explorer (version 6.0.2800, Windows 2000). In fact, the only “major” browser I can get it to work in is Safari on the Mac (version 2.0.4). Any ideas?

  • cakebaker

    @Josh Southern: I don’t know if it is fixed, probably not :|

  • Josh Southern

    So does the “AJAX multiple div updating” only work in Konqueror? That makes it pretty useless in the real world…

  • Josh Southern

    I figured out all the problems. Being new to this whole Prototype thing, I had downloaded the “latest version” of Prototype (1.4) from the Prototype website, then downloaded Scriptaculous. I just noticed that the Scriptaculous download contains the *actual* latest version of the Prototype library (1.5), and after uploading that file, everything works great in Firefox now.

    Sidenote – I could not get the updates to work in IE, either, and that ended up being a problem with my Norton Internet Security Popup Blocker. If I disable that, everything works perfect in IE as well.

  • cakebaker

    @Josh Southern: At least on my machine it works only with Konqueror. But Nate, the primary author of the Ajax helper, says in http://cakebaker.wordpress.com/2006/06/29/how-to-update-multiple-divs-with-ajax/#827 that it works with Firefox on his machines…

  • cake baker » Updating multiple divs with Ajax. Without crashing Firefox

    [...] In an earlier post I showed you how you can update multiple divs with Ajax. But there was one problem: it crashed Firefox on my machine, and other people reported the same problem, whereas it worked for others… [...]

  • Daniel Hofstetter (cakebaker): Updating multiple divs with Ajax. Without crashing Firefox

    [...] Daniel Hofstetter (cakebaker): Updating multiple divs with Ajax. Without crashing Firefox: ”In an earlier post I showed you how you can update multiple divs with Ajax. But there was one problem: it crashed Firefox on my machine, and other people reported the same problem, whereas it worked for others… [...]

  • cakebaker » Updating multiple divs with Ajax. Without crashing Firefox

    [...] In an earlier post I showed you how you can update multiple divs with Ajax. But there was one problem: it crashed Firefox on my machine, and other people reported the same problem, whereas it worked for others… [...]

  • Mark Ng

    be careful if you were thinking about putting $ajax->div() and $ajax->divEnd() into a element – they return strange errors that don’t point to the immediate problem.

  • misiek

    Its not really updating multiple div’s
    What if I have two tables and in each one one div. so Two divs in two tables, try now to update that divs with completely different data, try now to render different file in each div.???? is it possible ?
    I do not think so !!!!!!!!!!!!!!!!!!!!!!!!!!

  • cakebaker

    @misiek: Did you try it, or do you simply assume it doesn’t work? Imho it shouldn’t matter whether the divs are in a table or not.

  • sidr

    How can I update different divs with different urls when I click on ajaxed link? I have
    $options = array();
    $options['update'] = “breadcrumbs”;
    $options['url'] = “/tasks/breadcrumbs/”.$task['Task']['id'];
    $options['after'] = ‘new Ajax.Updater(\’task-tree\’,\’/tasks/tree\’, {asynchronous:true, evalScripts:true});';

    echo $ajax->link( $task['Task']['name'], ‘#’, $options );

    But when I click on this link doesn`t update task-tree but return the login form :(

  • sidr

    The problem was solved when I use $options[’loaded’] instead of $options[’after’]

  • cakebaker

    @sidr: I am glad you could solve the problem in the meantime :)

  • Tim Daldini

    What’s the point in using ajax div functions in favour of html div functions?

    I didnt really manage to understand the ajax helper code, but I think the ajax divs are conditionally rendered based on wheter or not an ajax request needs the div. Correct me if i’m wrong…

  • Zonium

    @Tim Daldini:
    Turn on firebug and see some js snippet gets generated by the ajax div functions – so I guess that’s the answer.

    var __ajaxUpdater__ = {first:”first%20div%20updated%3A%201196754195″,
    second:”second%20div%20updated%0D%0A”};
    for (n in __ajaxUpdater__) { if (typeof __ajaxUpdater__[n] == “string” && $(n)) Element.update($(n),
    unescape(decodeURIComponent(__ajaxUpdater__[n]))); }

  • cakebaker

    @Tim: See Zonium’s answer.

    @Zonium: Thanks for providing the answer!

  • gustavo bellino

    i have two views first.thtml and secundo.thtml
    when i call the function in the controller i put the

    $this->render('first', 'ajax');
    //for this example

    i also need to call the
    $this->render(‘second’, ‘ajax’);

    doing
    $this->render(‘first’, ‘ajax’);
    $this->render(‘second’, ‘ajax’);
    doesnt work

    some help?
    regards

  • gustavo bellino

    i could do it!!! :D
    i followed this link
    http://www.reversefolds.com/articles/show/ajax

    regards

  • cakebaker

    @gustavo: Cool to hear you got it done in the meantime :)

  • Kapil

    I have triggered multiple actions and updated multiple divs on single Ajax request by extending ajaxHelper

    <?php
    class MyAjaxHelper extends AjaxHelper {
    
    /**
     * Returns link to remote action
     *
     * Returns a link to a remote action defined by <i>options[url]</i>
     * (using the url() format) that's called in the background using
     * XMLHttpRequest. The result of that request can then be inserted into a
     * DOM object whose id can be specified with <i>options[update]</i>.
     *
     * Examples:
     * &lt;code&gt;
     *  link("Delete this post",
     * array("update" => "posts", "url" => "delete/{$postid->id}"));
     *  link(imageTag("refresh"),
     *		array("update" => "emails", "url" => "list_emails" ));
     * &lt;/code&gt;
     *
     * By default, these remote requests are processed asynchronous during
     * which various callbacks can be triggered (for progress indicators and
     * the likes).
     *
     * Example:
     * &lt;code&gt;
     *	link (word,
     *		array("url" => "undo", "n" => word_counter),
     *		array("complete" => "undoRequestCompleted(request)"));
     * &lt;/code&gt;
     *
     * The callbacks that may be specified are:
     *
     * - <i>loading</i>::		Called when the remote document is being
     *							loaded with data by the browser.
     * - <i>loaded</i>::		Called when the browser has finished loading
     *							the remote document.
     * - <i>interactive</i>::	Called when the user can interact with the
     *							remote document, even though it has not
     *							finished loading.
     * - <i>complete</i>:: Called when the XMLHttpRequest is complete.
     *
     * If you for some reason or another need synchronous processing (that'll
     * block the browser while the request is happening), you can specify
     * <i>options[type] = synchronous</i>.
     *
     * You can customize further browser side call logic by passing
     * in Javascript code snippets via some optional parameters. In
     * their order of use these are:
     *
     * - <i>confirm</i>:: Adds confirmation dialog.
     * -<i>condition</i>::	Perform remote request conditionally
     *                      by this expression. Use this to
     *                      describe browser-side conditions when
     *                      request should not be initiated.
     * - <i>before</i>::		Called before request is initiated.
     * - <i>after</i>::		Called immediately after request was
     *						initiated and before <i>loading</i>.
     *
     * @param string $title Title of link
     * @param string $href Href string "/products/view/12"
     * @param array $options		Options for JavaScript function
     * @param string $confirm		Confirmation message. Calls up a JavaScript confirm() message.
     * @param boolean $escapeTitle  Escaping the title string to HTML entities
     *
     * @return string				HTML code for link to remote action
     */
    	function link($title, $href = null, $options = array(), $confirm = null, $escapeTitle = true) {
    		if (!isset($href)) {
    			$href = $title;
    		}
    		if (!isset($options['url'])) {
    			$options['url'] = $href;
    		}		
    
    		if (isset($confirm)) {
    			$options['confirm'] = $confirm;
    			unset($confirm);
    		}
    		$htmlOptions = $this-&gt;__getHtmlOptions($options, array('url'));
    
    		if (empty($options['fallback']) || !isset($options['fallback'])) {
    			$options['fallback'] = $href;
    		}
    		$htmlOptions = array_merge(array('id' =&gt; 'link' . intval(rand()), 'onclick' =&gt; ''), $htmlOptions);
    
    		$htmlOptions['onclick'] .= ' event.returnValue = false; return false;';
    		$return = $this-&gt;Html-&gt;link($title, $href, $htmlOptions, null, $escapeTitle);
    //		pr($options);
    		if(is_array($options['url']) &amp;&amp; is_array($options['update']))
    		{
    			$script="";
    			$opts=array();
    			foreach($options['url'] as $key=&gt;$val){
    				$opts[$key]['url']=$val;
    			}
    			foreach($options['update'] as $key=&gt;$val){
    				$opts[$key]['update']=$val;
    				$opts[$key]['fallback']=$options['fallback'];
    				$opts[$key]['indicator']=$options['indicator'];
    				$script .= $this-&gt;Javascript-&gt;event("'{$htmlOptions['id']}'", "click", $this-&gt;remoteFunction($opts[$key]));
    			}
    		}		
    
    		if (is_string($script)) {
    			$return .= $script;
    		}
    		return $return;
    	}
    	
    	function submit($title = 'Submit', $options = array()) {
    		$htmlOptions = $this-&gt;__getHtmlOptions($options);
    		$htmlOptions['value'] = $title;
    
    		if (!isset($options['with'])) {
    			$options['with'] = 'Form.serialize(Event.element(event).form)';
    		}
    		if (!isset($htmlOptions['id'])) {
    			$htmlOptions['id'] = 'submit' . intval(rand());
    		}
    		
    		if(is_array($options['url']) &amp;&amp; is_array($options['update']))
    		{
    			$script="";
    			$opts=array();
    			foreach($options['url'] as $key=&gt;$val){
    				$opts[$key]['url']=$val;
    			}
    			foreach($options['update'] as $key=&gt;$val){
    				$opts[$key]['update']=$val;
    				$opts[$key]['class']=$options['class'];
    				$opts[$key]['with']=$options['with'];
    				$script .= $this-&gt;Javascript-&gt;event('"' . $htmlOptions['id'] . '"', 'click', $this-&gt;remoteFunction($opts[$key]));
    			}			
    		}
    		$htmlOptions['onclick'] = "event.returnValue = false; return false;";
    		return $this-&gt;Form-&gt;submit($title, $htmlOptions).$script;
    	}
    }
    ?&gt;
    
    // How to use
    
    submit('submit',array('url' =&gt; array('url 1 here','url 2 here'), 'update' =&gt; array('div 1 here','div 2 here')));	?&gt;
  • cakebaker

    @Kapil: Thanks for your comment!

    You might consider to open an enhancement ticket to include this functionality into the core AjaxHelper.

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License