IE javascript error - possibly related to setAttribute? - javascript

I am using Safalra's javascript to create a collapsible list. The script works across several browsers with no problem. However, when I apply the javascript to my own list, it fails to act as expected when I use IE (I'm using 7 at the moment). It simply writes the list, without the expand and contract images.
I copied the Safalra's javascript precisely, so I assume the error must be in my own list. This is how I generated my list:
<body onLoad="makeCollapsible(document.getElementById('libguides'));">
<ul id="libguides">
<script type="text/javascript" src="http://api.libguides.com/api_subjects.php?iid=54&more=false&format=js&guides=true&break=li"></script>
</ul>
(Yes, I do close the body tag eventually.) When I run this in IE, it tells me that line 48 is causing the problem, which appears to be:
node.onclick=createToggleFunction(node,list);
Here's the entire function:
function makeCollapsible(listElement){
// removed list item bullets and the sapce they occupy
listElement.style.listStyle='none';
listElement.style.marginLeft='0';
listElement.style.paddingLeft='0';
// loop over all child elements of the list
var child=listElement.firstChild;
while (child!=null){
// only process li elements (and not text elements)
if (child.nodeType==1){
// build a list of child ol and ul elements and hide them
var list=new Array();
var grandchild=child.firstChild;
while (grandchild!=null){
if (grandchild.tagName=='OL' || grandchild.tagName=='UL'){
grandchild.style.display='none';
list.push(grandchild);
}
grandchild=grandchild.nextSibling;
}
// add toggle buttons
var node=document.createElement('img');
node.setAttribute('src',CLOSED_IMAGE);
node.setAttribute('class','collapsibleClosed');
node.onclick=createToggleFunction(node,list);
child.insertBefore(node,child.firstChild);
}
I confess I'm too much of a javascript novice to understand why that particular line of code is causing the error. I looked at some of the other questions here, and was wondering if it might be a problem with setAttribute?
Thanks in advance.
Edited to add:
Here's the code for the createToggleFunction function. The whole of the script is just these two functions (plus declaring variables for the images).
function createToggleFunction(toggleElement,sublistElements){
return function(){
// toggle status of toggle gadget
if (toggleElement.getAttribute('class')=='collapsibleClosed'){
toggleElement.setAttribute('class','collapsibleOpen');
toggleElement.setAttribute('src',OPEN_IMAGE);
}else{
toggleElement.setAttribute('class','collapsibleClosed');
toggleElement.setAttribute('src',CLOSED_IMAGE);
}
// toggle display of sublists
for (var i=0;i<sublistElements.length;i++){
sublistElements[i].style.display=
(sublistElements[i].style.display=='block')?'none':'block';
}
}
}
Edited to add (again):
Per David's suggestion, I changed all instances of setAttribute & getAttribute...but clearly I did something wrong. IE is breaking at the 1st line (which is simply the doctype declaration) and at line 49, which is the same line of code where it was breaking before:
node.onclick=createToggleFunction(node,list);
Here's the first function as written now:
function makeCollapsible(listElement){
// removed list item bullets and the sapce they occupy
listElement.style.listStyle='none';
listElement.style.marginLeft='0';
listElement.style.paddingLeft='0';
// loop over all child elements of the list
var child=listElement.firstChild;
while (child!=null){
// only process li elements (and not text elements)
if (child.nodeType==1){
// build a list of child ol and ul elements and hide them
var list=new Array();
var grandchild=child.firstChild;
while (grandchild!=null){
if (grandchild.tagName=='OL' || grandchild.tagName=='UL'){
grandchild.style.display='none';
list.push(grandchild);
}
grandchild=grandchild.nextSibling;
}
// add toggle buttons
var node=document.createElement('img');
node.src = CLOSED_IMAGE;
node.className = 'collapsibleClosed';
node.onclick=createToggleFunction(node,list);
child.insertBefore(node,child.firstChild);
}
child=child.nextSibling;
}
}
And here's the second function:
function createToggleFunction(toggleElement,sublistElements){
return function(){
// toggle status of toggle gadget
// Use foo.className = 'bar'; instead of foo.setAttribute('class', 'bar');
if (toggleElement.className == 'collapsibleClosed') {
toggleElement.className = 'collapsibleOpen';
toggleElement.src = OPEN_IMAGE;
} else {
toggleElement.className = 'collapsibleClosed';
toggleElement.src = CLOSED_IMAGE;
}
// toggle display of sublists
for (var i=0;i<sublistElements.length;i++){
sublistElements[i].style.display=
(sublistElements[i].style.display=='block')?'none':'block';
}
}
}

Internet Explorer (until version 8, and then only in best standards mode) has a very broken implementation of setAttribute and getAttribute.
It effectively looks something like this:
function setAttribute(attribute, value) {
this[attribute] = value;
function getAttribute(attribute, value) {
return this[attribute];
}
This works fine iif the attribute name matches the property name, and the property takes a string value.
This isn't the case for the class attribute, where the matching property is className.
Use foo.className = 'bar'; instead of foo.setAttribute('class', 'bar');

node.onclick=createToggleFunction(node,list);
That is probably not what you want. Does createToggleFunction return a function? If it doesn't, then I bet you meant this:
node.onClick = function() { createToggleFunction(node, list); };
If my guess is right then the way you have it will set the onClick event handler to be the result of createToggleFunction, not a function like it needs to be.

Related

jQuery slideDown not working on element with dynamically assigned id

EDIT: I cleaned up the code a bit and narrowed down the problem.
So I'm working on a Wordpress site, and I'm trying to incorporate drop-downs into my menu on mobile, which means I have to use jQuery to assign classes and id's to my already existing elements. I have this code that already works on premade HTML, but fails on dynamically created id's.
Here is the code:
...
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
$(document).on('click', '.dropdown-title', function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
//THIS LINE FAILS
$(currentAttrValue).slideDown(300).addClass('d-open');
}
e.preventDefault();
});
I've registered the elements with the class dropdown-title using $(document).on(...) but I can't figure out what I need to do to register the elements with the custom ID's. I've tried putting the event callback inside the .each functions, I've tried making custom events to trigger, but none of them will get the 2nd to last line of code to trigger. There's no errors in the console, and when I console log the selector I get this:
[ul#m0.sub-menu.dropdown-content, context: document, selector: "#m0"]
0
:
ul#m0.sub-menu.dropdown-content
context
:
document
length
:
1
selector
:
"#m0"
proto
:
Object[0]
So jQuery knows the element is there, I just can't figure out how to register it...or maybe it's something I'm not thinking of, I don't know.
If you are creating your elements dynamically, you should be assigning the .on 'click' after creating those elements. Just declare the 'on click' callback code you posted after adding the ids and classes instead of when the page loads, so it gets attached to the elements with .dropdown-title class.
Check this jsFiddle: https://jsfiddle.net/6zayouxc/
EDIT: Your edited JS code works... There also might be some problem with your HTML or CSS, are you hiding your submenus? Make sure you are not making them transparent.
You're trying to call a function for a attribute, instead of the element. You probably want $(this).slideDown(300).addClass('d-active'); (also then you don't need $(this).addClass('d-active'); before)
Inside submenus.each loop add your callback listener.
As you are adding the class dropdown-title dynamically, it was not available at dom loading time, that is why event listener was not attached with those elemnts.
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
// add callback here
$(this).click( function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
$(currentAttrValue).slideDown(300).addClass('d-active');
}
e.preventDefault();
});
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
Turns out my problem is that jQuery is adding to both the mobile menu and the desktop menu, where the desktop menu is being loaded first when I search for that ID that's the one that jQuery finds. So it turns out I was completely wrong about my suspicions.

How do I iterate through div's child elements and hide them?

I have a div that have a few elements that I want to hide, on users request. Those elements have a particular background color. The call of the function is working (it is associated to a checkbox) but it just doesnt do what i want. Actually, it does nothing. This is what I've got:
function toogleDisplay()
{
var kiddos= document.getElementById('external-events').childNodes; //my div
for(i=0; i < kiddos.length; i++)
{
var a=kiddos[i];
if (a.style.backgroundColor=="#A2B5CD")
{
if (a.style.display!="none")
{
a.style.display='none';
}
else
{
a.style.display='block';
}
}
}
}
What am I doing wrong?
An element's background colour is converted to rgb() (or rgba()) format internally.
But that aside, assuming $ is jQuery (you haven't tagged your question so I don't know!) then a is a jQuery object, which does not have a style property. It looks like you just wanted var a = kiddos[i];.
It is more reliable to use a specific class name instead.
You re wrapping your kiddos[i] in a jquery-object $(kiddos[i]) and then try to access the normal properties of a html-dom-objekt.
You have 2 possibilities:
remove the $()
use jquery-access to the properties
a.css('display', none); // or just a.hide();
Additionally you cant check for '#123456' since the color is transformed. Check (#Niet the Dark Absol)s answer for this
I would suggest adding a class to the elements you want to check. Then instead of trying to use background, you can do
$(kiddos[i]).hasClass('myclass')
or for a very efficient way, you can do it in one line of code.
function toogleDisplay()
{
$('.myclass').toggle(); //this will toggle hide/show
}
The divs would look like this
<div class='myclass'>Content</div>
EDIT - to do it without modifying existing html. I also think the rbg color should be rgb(162, 181, 205) if im not mistaken.
You can try something like this. Its based off the following link
Selecting elements with a certain background color
function toogleDisplay()
{
$('div#external-events').filter(function() {
var match = 'rgb(162, 181, 205)'; // should be your color
return ( $(this).css('background-color') == match );
}).toggle()
}
Your jquery selection of a is causing issues. Unwrap the $() from that and you should be fine.
Also you could end up selecting text nodes that wont have a style property. You should check that the style property exists on the node before trying to access background, display, etc.
Use a class instead of a background and check for that instead.
i think you need to see if the 'nodeType' is an element 'a.nodeType == 1' see Node.nodeType then it will work over multiple lines
var kiddos= document.getElementById('external-events').childNodes; //my div
for(i=0; i < kiddos.length; i++)
{
var a=kiddos[i];
if (a.nodeType == 1){ // Check the node type
if (a.style.backgroundColor=="red")
{
if (a.style.display!="none")
{
a.style.display='none';
}
else
{
a.style.display='block';
}
}
}
}
I decided to go for another aproach, using the idea of Kalel Wade. All the elements that may be (or not) hidden, already had a class name, which were the same for all elements, fortunately.
here comes the code
function toogleDisplay()
{
var kiddos = document.getElementsByClassName("external-event ui-draggable");
for (var i = 0, len = kiddos.length; i < len; i++) {
var a=kiddos[i];
if (a.style.backgroundColor==="rgb(162, 181, 205)")
{
if (a.style.display!="none")
{
a.style.display='none';
}
else
{
a.style.display='block';
}
}
}
}

jQuery - remove li from array with delete image

I'm attempting to make a menu bar that can have <li> elements added and removed. So far so good, but when I try and remove them I'm running into issues. I've toyed with this for a couple hours and now I'm wondering if this whole process could just be made easier (maybe an object?).
Anyways, here's the full code (80 lines), with comments to follow along.
var tabs = $('.accountSelectNav');
var titles = [];
var listItems = [];
// when the page loads check if tabs need to be added to the ul (menu bar)
$(document).ready(function(e) {
if ($.cookie('listItems') != null) {
console.log('not null');
//return "listItems" to it's array form.
listItems = JSON.parse($.cookie('listItems'));
$('.accountSelectNav').append(listItems);
}
});
$('.selectTable td:first-child').on('click', function(e) {
$('#home_select').removeClass('navHighlight');
//grab the text value of this cell
title = $(this).text();
$.ajax({
url:'core/functions/getAccountId.php',
type: 'post',
data: {'title' : title}
}).fail (function() {
alert('error');
}).done(function(data) {
accountId = $.trim(data);
// store values in the cookie
$.cookie('account_id', accountId, {expires : 7});
$.cookie('title', title, {expires : 7});
window.location = ('home_table.php');
});
// make sure the value is NOT currently in the array. Then add it
var found = jQuery.inArray(title, titles);
if (found == -1) {
titles.push(title);
addTab();
}
// make sure the value is NOT currently in the array. Then add it
found = jQuery.inArray(title, listItems);
if (found == -1) {
addListItem();
//place <li>'s in cookie so they may be used on multiple pages
$.cookie('listItems', JSON.stringify(listItems));
};
});
$("body").on("click", ".deleteImage", function (e) {
var removeTitle = $(this).closest('li').find('a').text();
var removeItem = $(this).closest('li')[0].outerHTML;
//remove title from "titles" array
titles = jQuery.grep(titles, function (value) {
return value != removeTitle;
});
//remove <li> from "listItems" array
listItems = jQuery.grep(listItems, function (value) {
return value != removeItem;
});
// this shows the <li> is still in the listItemsarray
console.log(listItems);
// put the array back in the cookie
$.cookie('listItems', JSON.stringify(listItems));
removeTab(this);
});
$("body").on("mouseover", ".accountSelectNav li", function(e) {
$(this).find('.deleteImage').show();
});
$("body").on("mouseleave", ".accountSelectNav li", function(e) {
$(this).find('.deleteImage').hide();
});
function addTab() {
tabs.append('<li class="navHighlight">' + '' + title + '' + '' + '<img src="images/delete.png" class="deleteImage"/>' + '' + '</li>');
};
function removeTab(del) {
$(del).closest('li').remove();
}
function addListItem() {
var s = ('<li class="navHighlight">' + '' + title + '' + '' + '<img src="images/delete.png" class="deleteImage"/>' + '' + '</li>');
listItems.push(s);
}
So you see I have two arrays of equal length that should always be the same length. One stores the title to be displayed in the tab, the other holds the html for the <li> which will be appended to the <ul>. I have no problem removing the title from its array. However removing the <li> from it's array is becoming a rather big hassle. You see when I get the <li> element after its been inflated the html inside does not exactly match what was put in, the browser adds style elements.
Example, the variable "removeItem" represents the html value of the selected <li> I wish to remove. It looks like this:
<li class="navHighlight">Test1<img src="images/delete.png" class="deleteImage" style="display: inline;"></li>
yet the value in my array "listItems" looks like this:
<li class="navHighlight">Test1<img src="images/delete.png" class="deleteImage"/></li>
So my attempt at removing it from my array always fails because they aren't a perfect match.
Now my question is how do I remove this <li> item? Also is there an easier way to do this whole process and I'm just not seeing it?
Thanks for your time.
EDIT
Fiddle by request here
Easiest way I can explain it.
Click the link to the fiddle.
Click any cell in the "App Name" column
This will add a <li> to the <ul> (menu) above of the table
When you hover over the <li> a picture appears
Click the picture
This should remove the <li>, both from the <ul> and from the array listItems
right now it does not
In the process of making this easier to check, I've taken your JSFiddle and did the following:
removed extra console.log and comments
removed interaction with cookies (since I did not have them in the first place, I figured they wouldn't just the first scenario)
After doing so I reached a point (you can see it here) where the desired functionality just works.
I even went ahead and removed the ajax stuff because that alert was driving me crazy. (here)
Since this works fine, my guess is that your issue lies between the lines that I removed.
Your usage of cookies is as follows:
To load existing tabs and add them back again
To save account_id and title, which is not used back again
To persist the listItems after a new item has been added
I then opened up the console with your version of the fiddle and the execution of javascript stops at $.cookie() with the error undefined is not a function.
This clearly indicates that the issue present in the Fiddle is that jQuery.cookie is not present and so those calls are halting the execution of the rest of your script. This also explains why it just started working when I took them out.
I posted the whole process of how I got there to indicate how I trimmed down the problem to specific parts, which is useful to reduce the problem space. When you're out of options and reach a place when you're lost, it's easier to post a question with less code and the specific part of the problem that you've identified. This will help you in finding the issues that you're facing and StackOverflow to provide proper answers to your questions.
Hope it helps!
Here is the solution I came up with. It should be much easier for people to understand than my original post. Although it's a long read it may be worth it, especially for new developers.
The point of this code is to make a menu bar out of an un-ordered list or <ul>. The menu bar needs to be used on multiple pages. So I'll be using cookies.
I start with this code to get a text value from my table.:
$('.selectTable td:first-child').on('click', function(e) {
// This value will be used later for the name of the tab or `<li>` inside our menu bar or `<ul>`
title = $(this).text();
});
Then I place the value in an array. I do this only if the array does not already have this string inside it. I do not want duplicates:
var found = jQuery.inArray(title, titles);
var titles = [];
if (found == -1) {
titles.push(title);
}
Then I store the array into a cookie, using a library like this:
$.cookie('titles', JSON.stringify(titles));
Now when any page loads that needs this menu bar I run this code to check if there are any values:
$(document).ready(function() {
if ($.cookie('titles') != null) {
titles = JSON.parse($.cookie('titles'));
}
});
Now I need to loop through the array. When I loop through the array I have to do 3 things:
1) Grab the string value.
2) Add the html to my new string so it becomes a list item or <li>.
3) Append the newly created <li> to our <ul>.
Like so:
for(var i = 0; i < titles.length; i++) {
var str = titles[i];
var listItem = '<li class="navHighlight">'
+ '<a href="#">'
+ str
+ '</a>'
+ '<a href="#">'
+ '<img src="images/delete.png" class="deleteImage"/>'
+ '</a>'
+ '</li>';
$('.accountSelectNav').append(listItem);
}
Now, if I want to remove this <li> I click the delete image found inside our <li>. What delete image you say? Look at the html I added again. You will see I add an <img> tag in there.
Now delete like so:
$("body").on("click", ".deleteImage", function (e) {
// grabs the text value of my li, which I want to remove
var removeTitle = $(this).closest('li').find('a').text();
// runs through my titles array and returns an array without the value above
titles = jQuery.grep(titles, function (value) {
return value != removeTitle;
});
});
Then I simply place the new array inside my cookie once again. Like this:
$.cookie('titles', JSON.stringify(titles));
And finally I remove the tab like this:
removeTab(this);
function removeTab(del) {
$(del).closest('li').remove();
}
Yay, I'm done. So now, if anyone has a more elegant way of accomplishing this I'm listening. I have no doubt there's a better way, javascript/jQuery isn't even close to my strong point.
The full code can be found here.

CKEditor default style for styleCommand (format styles with buttons)

I have created a CKEditor plugin which does the base p, h2, h3, h4 formatting with custom buttons (instead of the stylescombo). It works great, but if I uncheck an element (ex. 'h2'), sets the 'div' tag as parent element for the row. I want to be the 'p' as the default element and also the 'p' button can't be unchecked (unless I clicking on another, ex. 'h2' button). How is this possible?
The plugin looks like:
CKEDITOR.plugins.add('stylesbuttons_custom',{
lang:'en',
icons:'p,h2,h3,h4',
init:function(editor){
var order=0;
var addButtonCommand=function(buttonName,buttonLabel,commandName,styleDefiniton){
if (!styleDefiniton)
return;
var style=new CKEDITOR.style(styleDefiniton);
editor.attachStyleStateChange(style,function(state){
!editor.readOnly && editor.getCommand(commandName).setState(state);
});
editor.addCommand(commandName,new CKEDITOR.styleCommand(style));
if (editor.ui.addButton){
editor.ui.addButton(buttonName,{
label:buttonLabel,
command:commandName,
toolbar:'basicstyles,'+(order+=10)
});
}
};
var lang=editor.lang.stylesbuttons_custom;
addButtonCommand('P',lang.p,'p',{element:'p'});
addButtonCommand('H2',lang.h2,'h2',{element:'h2'});
addButtonCommand('H3',lang.h3,'h3',{element:'h3'});
addButtonCommand('H4',lang.h4,'h4',{element:'h4'});
}
});
I load the plugin like:
config.extraPlugins='stylesbuttons_custom';
I put buttons to toolbar like:
config.toolbar:[['P','H2','H3','H4','Pre']];
Here is a screenshot about the problem:
Cross posting my answer from CKEditor forum.
I think that you need to write your own command instead of using CKEDITOR.styleCommand.
It should work exactly like CKEDITOR.styleCommand when style is not yet applied on current selection.
But when clicked again it should apply the paragraph style, not remove the previously applied style. E.g:
styleCommand.prototype.exec = function( editor ) {
editor.focus();
if ( this.state == CKEDITOR.TRISTATE_OFF )
editor.applyStyle( this.style );
else if ( this.state == CKEDITOR.TRISTATE_ON )
editor.applyStyle( paragraphStyle );
};
PS. I created a ticket: http://dev.ckeditor.com/ticket/10190 because I think that removing block style should revert back to paragraph (in enterMode=P). For now use the above workaround.
Yes, #Reinmar notifies that there is an error in the CKEditor's style.js where the this._.enterMode is not defined.
Doing this on style.js, resolve the problem:
this._ = {
definition: styleDefinition,
enterMode: CKEDITOR.config.enterMode
};
And from now on when a style button is unchecked the block changes to the default 'p' element.
Now my complete working plugin looks like:
(function(){
CKEDITOR.plugins.add('custombuttons',{
lang:'hu,en,de,ro',
init:function(editor){
var order=0,t=this,lang=editor.lang.custombuttons;
// addButtonCommand helper
var addButtonCommand=function(buttonName,buttonLabel,commandName,styleDefiniton){
var style=new CKEDITOR.style(styleDefiniton);
var styleCommand=function(style){
this.style=style;
this.allowedContent=style;
this.requiredContent=style;
this.contextSensitive=true;
};
styleCommand.prototype={
exec:function(editor){
editor.focus();
if (this.state==CKEDITOR.TRISTATE_OFF)
editor.applyStyle(this.style);
else if (this.state==CKEDITOR.TRISTATE_ON)
editor.removeStyle(this.style);
if(commandName!='fakecommand'){editor.execCommand('fakecommand');editor.execCommand('fakecommand');} /* hack to change button state properly */
},
refresh:function(editor,path){
this.setState(path&&this.style.checkApplicable(path)?(this.style.checkActive(path)?CKEDITOR.TRISTATE_ON:CKEDITOR.TRISTATE_OFF):CKEDITOR.TRISTATE_DISABLED);
}
};
editor.addCommand(commandName,new styleCommand(style));
if(editor.ui.addButton){editor.ui.addButton(buttonName,{label:buttonLabel,command:commandName,toolbar:'basicstyles,'+(order+=10),icon:t.path+'images/'+commandName+'.png'});}
};
// _fakebutton (hack)
addButtonCommand('_fakebutton','','fakecommand',{element:'span'});
// style buttons
addButtonCommand('P',lang.p,'p',{element:'p'});
addButtonCommand('H2',lang.h2,'h2',{element:'h2'});
addButtonCommand('H3',lang.h3,'h3',{element:'h3'});
addButtonCommand('H4',lang.h4,'h4',{element:'h4'});
addButtonCommand('Pre',lang.pre,'pre',{element:'pre'});
addButtonCommand('Mini',lang.mini,'mini',{element:'p',attributes:{class:'mini'}});
addButtonCommand('Important',lang.important,'important',{element:'span',attributes:{class:'important'}});
addButtonCommand('Comment',lang.comment,'comment',{element:'span',attributes:{class:'comment'}});
addButtonCommand('Mark',lang.mark,'mark',{element:'mark'});
addButtonCommand('ImgLeft',lang.imgLeft,'imgLeft',{element:'img',attributes:{class:'imgleft'}});
addButtonCommand('ImgRight',lang.imgRight,'imgRight',{element:'img',attributes:{class:'imgright'}});
addButtonCommand('ImgCenter',lang.imgCenter,'imgCenter',{element:'img',attributes:{class:'imgcenter'}});
// button shortcut keys
editor.setKeystroke(
[
[CKEDITOR.CTRL+48,'p'], // Ctrl+0
[CKEDITOR.CTRL+49,'h2'], // Ctrl+1
[CKEDITOR.CTRL+50,'h3'], // Ctrl+2
[CKEDITOR.CTRL+51,'h4'], // Ctrl+3
]);
}
});
})();
There is still a hack in the code. I needed to run a 'fakecommand' to really update (refilter?) the changed tags and all its parent tags. For example the 'p.mini' button caused problems (the state was not updated) when clicked multiple time. So there is still an unelegant solution. Any idea how to force to update or refilter the code after a style is applied?

Can someone explain the following javascript code?

In addition to the explanation, what does the $ mean in javascript? Here is the code:
var ZebraTable = {
bgcolor: '',
classname: '',
stripe: function(el) {
if (!$(el)) return;
var rows = $(el).getElementsByTagName('tr');
for (var i=1,len=rows.length;i<len;i++) {
if (i % 2 == 0) rows[i].className = 'alt';
Event.add(rows[i],'mouseover',function() {
ZebraTable.mouseover(this); });
Event.add(rows[i],'mouseout',function() { ZebraTable.mouseout(this); });
}
},
mouseover: function(row) {
this.bgcolor = row.style.backgroundColor;
this.classname = row.className;
addClassName(row,'over');
},
mouseout: function(row) {
removeClassName(row,'over');
addClassName(row,this.classname);
row.style.backgroundColor = this.bgcolor;
}
}
window.onload = function() {
ZebraTable.stripe('mytable');
}
Here is a link to where I got the code and you can view a demo on the page. It does not appear to be using any framework. I was actually going through a JQuery tutorial that took this code and used JQuery on it to do the table striping. Here is the link:
http://v3.thewatchmakerproject.com/journal/309/stripe-your-tables-the-oo-way
Can someone explain the following
javascript code?
//Shorthand for document.getElementById
function $(id) {
return document.getElementById(id);
}
var ZebraTable = {
bgcolor: '',
classname: '',
stripe: function(el) {
//if the el cannot be found, return
if (!$(el)) return;
//get all the <tr> elements of the table
var rows = $(el).getElementsByTagName('tr');
//for each <tr> element
for (var i=1,len=rows.length;i<len;i++) {
//for every second row, set the className of the <tr> element to 'alt'
if (i % 2 == 0) rows[i].className = 'alt';
//add a mouseOver event to change the row className when rolling over the <tr> element
Event.add(rows[i],'mouseover',function() {
ZebraTable.mouseover(this);
});
//add a mouseOut event to revert the row className when rolling out of the <tr> element
Event.add(rows[i],'mouseout',function() {
ZebraTable.mouseout(this);
});
}
},
//the <tr> mouse over function
mouseover: function(row) {
//save the row's old background color in the ZebraTable.bgcolor variable
this.bgcolor = row.style.backgroundColor;
//save the row's className in the ZebraTable.classname variable
this.classname = row.className;
//add the 'over' class to the className property
//addClassName is some other function that handles this
addClassName(row,'over');
},
mouseout: function(row) {
//remove the 'over' class form the className of the row
removeClassName(row,'over');
//add the previous className that was stored in the ZebraTable.classname variable
addClassName(row,this.classname);
//set the background color back to the value that was stored in the ZebraTable.bgcolor variable
row.style.backgroundColor = this.bgcolor;
}
}
window.onload = function() {
//once the page is loaded, "stripe" the "mytable" element
ZebraTable.stripe('mytable');
}
The $ doesn't mean anything in Javascript, but it's a valid function name and several libraries use it as their all-encompassing function, for example Prototype and jQuery
From the example you linked to:
function $() {
var elements = new Array();
for (var i=0;i<arguments.length;i++) {
var element = arguments[i];
if (typeof element == 'string') element = document.getElementById(element);
if (arguments.length == 1) return element;
elements.push(element);
}
return elements;
}
The $ function is searching for elements by their id attribute.
This function loops through the rows in a table and does two things.
1) sets up alternating row style. if (i % 2 == 0) rows[i].className = 'alt' means every other row has its classname set to alt.
2) Attaches a mouseover and mouseout event to the row so the row changes background color when the user mouses over it.
the $ is a function set up by various javascript frameworks ( such as jquery) that simply calls document.getElementById
The code basically sets alternating table rows to have a different CSS class, and adds a mouseover and mouseout event change to a third css class, highlighting the row under the mouse.
I'm not sure if jQuery, prototype or maybe another third party JS library is referenced, but the dollar sign is used by jQuery as a selector. In this case, the user is testing to see if the object is null.
$ is the so-called "dollar function", used in a number of JavaScript frameworks to find an element and/or "wrap" it so that it can be used with framework functions and classes. I don't recognize the other functions used, so I can't tell you exactly which framework this is using, but my first guess would be Prototype or Dojo. (It certainly isn't jQuery.)
The code creates a ZebraTable "object" in Javascript, which stripes a table row by row in Javascript.
It has a couple of member functions of note:
stripe(el) - you pass in an element el, which is assumed to be a table. It gets all <tr> tags within the table (getElementsByTagName), then loops through them, assigning the class name "alt" to alternating rows. It also adds event handlers for mouse over and mouse out.
mouseover(row) - The "mouse over" event handler for a row, which stores the old class and background colour for the row, then assigns it the class name "over"
mouseout(row) - The reverse of mouseover, restores the old class name and background colour.
The $ is a function which returns an element given either the elements name or the element itself. It returns null if its parameters are invalid (non-existent element, for example)
I believe the framework being used is Prototype, so you can check out their docs for more info
Have a look at the bottom of the article that you have got the code from, you'll see that they say you'll also need prototype's $ function. From article
In your CSS you’ll need to specify a
default style for table rows, plus
tr.alt and tr.over classes. Here’s a
simple demo, which also includes the
other functions you’ll need (an Event
registration object and Prototype’s $
function).

Categories

Resources