How to update multiple divs with Ajax

Published on June 29, 2006 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 June 29, 2006 at 16:30

    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 June 29, 2006 at 17:47

    @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 June 29, 2006 at 18:56

    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 June 29, 2006 at 20:44

    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 June 30, 2006 at 02:21
  • Stefano June 30, 2006 at 10:19

    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 June 30, 2006 at 13:22

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

  • Josh Southern August 14, 2006 at 17:15

    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 August 15, 2006 at 08:54

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

  • Josh Southern August 16, 2006 at 17:13

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

  • Josh Southern August 16, 2006 at 18:11

    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 August 16, 2006 at 18:12

    @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 August 18, 2006 at 08:38

    [...] 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 August 19, 2006 at 02:12

    [...] 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 October 01, 2006 at 09:25

    [...] 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 November 28, 2006 at 13:03

    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 May 14, 2007 at 22:07

    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 May 16, 2007 at 10:19

    @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 October 31, 2007 at 10:44

    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 October 31, 2007 at 11:05

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

  • cakebaker November 01, 2007 at 17:51

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

  • Tim Daldini December 04, 2007 at 04:39

    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 December 04, 2007 at 09:50

    @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 December 04, 2007 at 18:55

    @Tim: See Zonium’s answer.

    @Zonium: Thanks for providing the answer!

  • gustavo bellino April 18, 2008 at 15:44

    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 April 18, 2008 at 16:11

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

    regards

  • cakebaker April 18, 2008 at 17:08

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

  • Kapil August 21, 2008 at 16:15

    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 August 22, 2008 at 17:59

    @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