How to modify static javascript data in Drupal
We can add javascript or javascript settings in drupal by calling drupal_add_js(), in some cases you may want to remove some javascript you add earlier.
There is no built in function like drupal_remove_js() to do that.
The same applies to javascript settings which are stored Drupal.settings, what if we want to remove some settings that are added in previous processes? Adding a setting multiple times might have some unexpected consequences.
Javascript settings are used when you want to pass server data to javascript in the front end. For example if I want to pass a pair of geo coordinates from database to map initialization function in javascript, I would like to save coordinates in Drupal.settings then in behavior attach function to use them.
Adding server data to javascript settings:
// set the central point
$lonlat = array(
'lon' => $lon,
'lat' => $lat,
);
// add it to Drupal.settings
drupal_add_js(array(‘mySetting’ => array('center'=> $lonlat)), array('type' => 'setting'));
All javascripts and settings of a request are stored in a static data store($javascript), here is what happens when a javascript setting is added by drupal_add_js(),
case 'setting':
// All JavaScript settings are placed in the header of the page with
// the library weight so that inline scripts appear afterwards.
$javascript['settings']['data'][] = $data;
break;
As you can see drupal_add_js() does not check if the same settings has already existed, it just simply appends it to $javascript[‘settings’][‘data’] array, adding a setting multiple times will end up copies of settings in static javascript array $javascript.
This only happens to settings, for javascripts it is keyed by javascript name, it will not have multiple copies.
When javascript is finally output to page in drupal_get_js():
case 'setting':
$js_element = $element;
$js_element['#value_prefix'] = $embed_prefix;
$js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(drupal_array_merge_deep_array($item['data'])) . ");";
$js_element['#value_suffix'] = $embed_suffix;
$output .= theme('html_tag', array('element' => $js_element));
break;
It does do some array merging, so you will not get two copies in Drupal.settings, but it is hard to predict which version of setting in $javascript actually made its way to Drupal.settings. and it is not an ideal situation to have multiple copies in $javascript in the first place.
Ideally drupal_add_js() would have prevented this by checking the existence of a settings before it is added.
Also We cannot find any built in functions that allow us to modify static $javascript. Here I want to share with you a function that I did to remove a customer setting from $javascript.
function _remove_js_settings($settingKey)
{
$javascript = &drupal_static('drupal_add_js');
foreach ($javascript['settings']['data'] as $key => $item)
{
if (array_key_exists($settingKey, $item)) {
unset($javascript['settings']['data'][$key]);
}
}
return $javascript;
}
The only trick is calling &drupal_static() rather than calling drupal_add_js() as the former gives you a pointer and later is giving same list as a copy, the list you get from drupal_add_js() is a copy of static data, so changes to the copy are not persisted to static data store.
I am sure from this example you can work out how to remove javascript or even change css settings from its static cache, bearing in mind that css is stored at ‘drupal_add_css’
[Added 18 Oct 2013]
If a setting does need to have multiple values, it is better to put them in a keyed array, a good example is system settings.ajax which is keyed by element id
$element['#attached']['js'][] = array(
'type' => 'setting',
'data' => array('ajax' => array($element['#id'] => $settings)),
);
Tags: drupal

Good Job!