5

I have following code.

HTML is below.

<div class="normal">
    <p>This is  Paragraph No.1</p>
    <p>This is  Paragraph No.2</p>
    <p>This is  Paragraph No.3</p>
    <p>This is  Paragraph No.4</p>
    <p>This is  Paragraph No.5</p>           
</div>

CSS is below

.normal {
    color: #808080;
    border: 4px solid blue;
    border-radius: 50px 50px;
    width: 800px;
    font-family: 'Comic Sans MS';
    margin: auto;
    margin-top: 10px;
    font-size: 30px;
    -webkit-transform: rotate(10deg);
}

.change {
    color:#ffd800;
    border: 6px solid orange;
    border-radius: 50px 50px;
    width: 800px;
    font-family: 'Comic Sans MS';
    margin: auto;
    margin-top: 10px;
    font-size: 30px;
    -webkit-transform: rotate(20deg);
}

What I want is to toggle my div class between normal and change whenever i click inside the div element. I know how to do it using jQuery but i want to use pure javascript?

Following is my try

(function () {
    var pElement = document.getElementsByClassName("normal");
    pElement.onclick = function () {
       //what to do here
    };
} ());
1
  • 1
    pElement is an array of HTMLElements, so adding onclick will not have any effect. You will have to loop through pElement and add an event listener for each element. Commented May 12, 2014 at 20:28

4 Answers 4

7

getElementsByClassName returns a list of elements, not a single element. So you'll want to get the first element from it, which actually refers to your div. The code should look something like this:

var pElements = document.getElementsByClassName("normal");
var pElement = pElements[0];
    pElement.onclick = function () {
       if (this.getAttribute("class") == "normal")
         this.setAttribute("class", "change")
       else
         this.setAttribute("class", "normal");
    };

Demo: http://jsfiddle.net/2QqU5/

As RobG mentioned, document.getElementsByClassName() isn't supported by older browsers still in use. This is mainly IE8 and below. As an alternative, you can use document.querySelectorAll(".normal"). Notice the . in front of the classname (it is a CSS selector). Since you only need one element, you can also use document.querySelector(".normal"), to get just that one element. This might actually be easier, since these are the selectors that jQuery uses as well, so it might be easier to switch back and forth between native an jQuery.

And you can set the class using the className property, instead of using get/setAttribute.

Bringing all that together, the updated code looks like this:

var pElement = document.querySelector(".normal");
    pElement.onclick = function () {
       if (this.className == "normal")
         this.className = "change";
       else
         this.className = "normal";
    };

Updated demo: http://jsfiddle.net/2QqU5/2/

Sign up to request clarification or add additional context in comments.

4 Comments

Very nice and Thank you very much indeed.
Except that getElementsByClassName isn't supported by all browsers in use. :-(
@RobG Thanks. You're right and IE8 doesn't support it. The alternative is using document.querySelector(). I've added it to my answer. Thanks again.
Also, getting and setting a className using get/setAttribute isn't necessarily going to work if the class was set using a property, so it's better to always use properties unless you have a very good reason to use attribtues (and it's less to type and uses direct property access rather than a generic method so should be faster).
6

If browser support isn't an issue, you could use the toggle() method/classList.

Example Here

(function () {
    var pElement = document.getElementsByClassName("normal")[0];
    pElement.addEventListener('click',function(){
       pElement.classList.toggle('change');
    });
} ());

Browser support for classList.

4 Comments

If browser support isn't an issue, I'd rather use addEventListener :)
And, just to help considerations, here's a compatibility table: caniuse.com/#search=classlist
To cut to the chase here, .classList requires at least IE 10.
Yes! Give me a modern environment where the likes of IE<10 are dead and buried! :)
0
(function () {
    var pElements = document.getElementsByClassName("normal");

    for ( p in pElements ) {

        p.onclick = function () {
            if ( this.className.indexOf('normal') > -1 ) {
                this.className = 'change';
            } else {
                this.className = 'normal';
            }
        };

    }
} ());

Haven't tested yet but this should do the trick.

1 Comment

Because you enumerate pElements rather than using array iteration methods, p will pick up properties like length and item from the NodeList/HTMLCollection, which are enumerable. Whoops!
0

If you want something that is cross browser, with pure javascript (not using a library like jQuery), then you will need something like this (which is a similar idea to @JoshCrozier answer but with a load of compatibility functions instead thrown in, which will default to the natives if they are available. Has a UMD at the bottom to allow you to use module loaders so that you can keep these in a separate file - a library). === fun what? :)

Javascript - the compatability bits

/*global window, document, module, define, $ */

(function () {
    'use strict';

    var commands = {},
        MAX_UINT32 = 4294967295,
        baseFunction = function () {
            return;
        },
        privateUndefined = (baseFunction()),
        hasOwn = commands.hasOwnProperty,
        toClass = commands.toString,
        trimFN = ''.trim,
        baseArray = [],
        slice = baseArray.slice,
        forEachFN = baseArray.forEach,
        filterFN = baseArray.filter,
        $ = {},
        makeRegex;

    function isFunction(arg) {
        return typeof arg === 'function';
    }

    function throwIfNotFunction(arg) {
        if (!isFunction(arg)) {
            throw new TypeError('is not a function');
        }

        return arg;
    }

    function isUndefined(arg) {
        return privateUndefined === arg;
    }

    function isNull(arg) {
        return null === arg;
    }

    function isUndefinedOrNull(arg) {
        return isUndefined(arg) || isNull(arg);
    }

    function isObject(arg) {
        return toClass.call(arg) === '[object Object]';
    }

    function isString(arg) {
        return typeof arg === 'string';
    }

    function isNumber(arg) {
        return typeof arg === 'number';
    }

    function isBoolean(arg) {
        return typeof arg === 'boolean';
    }

    function handler(object, evt, func) {
        var ret;

        if (evt) {
            ret = func.call(object, evt);
            if (false === ret) {
                evt.stopPropagation();
                evt.preventDefault();
            }
        } else {
            window.event.target = window.event.srcElement;
            ret = func.call(object, window.event);
            if (false === ret) {
                window.event.returnValue = false;
                window.event.cancelBubble = true;
            }
        }

        return ret;
    }

    $.addEventListener = function (object, type, func) {
        var uid = type + ':' + func,
            euid = 'e:' + uid;

        object[euid] = func;
        if (isFunction(object.addEventListener)) {
            object[uid] = function (evt) {
                handler(object, evt, object[euid]);
            };

            object.addEventListener(type, object[uid], false);
        } else if (isObject(object.attachEvent)) {
            object[uid] = function () {
                handler(object, null, object[euid]);
            };

            object.attachEvent('on' + type, object[uid]);
        } else {
            throw new Error('Handler could not be added.');
        }
    };

    $.removeEventListener = function (object, type, func) {
        var uid = type + ':' + func,
            euid = 'e:' + uid;

        if (isFunction(object.removeEventListener)) {
            object.removeEventListener(type, object[uid], false);
        } else if (isObject(object.detachEvent)) {
            object.detachEvent('on' + type, object[uid]);
        } else {
            throw new Error('Handler could not be removed.');
        }

        object[euid] = null;
        object[uid] = null;
        delete object[euid];
        delete object[uid];
    };

    if (isFunction(trimFN)) {
        $.trim = function (text) {
            return trimFN.call(text);
        };
    } else {
        $.trim = function (text) {
            return text.replace(/^\s+|\s+$/g, '');
        };
    }

    if ('classList' in document.body) {
        $.classList = {
            contains: function (node, className) {
                return node.classList.contains(className);
            },

            add: function add(node, className) {
                node.classList.add(className);
            },

            remove: function (node, className) {
                node.classList.remove(className);
            },

            toggle: function (node, className) {
                node.classList.toggle(className);
            }
        };
    } else {
        makeRegex = function (className, flags) {
            return new RegExp('(?:^|\\s)' + className + '(?!\\S)', isString(flags) ? flags : '');
        };

        $.classList = {
            contains: function (node, className) {
                return !!node.className.match(makeRegex(className));
            },

            add: function add(node, className) {
                if (!$.classList.contains(node, className)) {
                    node.className = $.trim(node.className);
                    if (node.className) {
                        node.className += ' ';
                    }

                    node.className += className;
                }
            },

            remove: function (node, className) {
                if ($.classList.contains(node, className)) {
                    node.className = $.trim(node.className.replace(makeRegex(className, 'g'), ''));
                }
            },

            toggle: function (node, className) {
                if ($.classList.contains(node, className)) {
                    $.classList.remove(node, className);
                } else {
                    $.classList.add(node, className);
                }
            }
        };
    }

    function checkObjectCoercible(inputArg) {
        if (isUndefinedOrNull(inputArg)) {
            throw new TypeError('Cannot convert "' + inputArg + '" to object');
        }

        return inputArg;
    }

    function argToObject(inputArg) {
        var object = checkObjectCoercible(inputArg);

        if (isBoolean(object) || isNumber(object) || isString(object)) {
            object = commands.constructor(object);
        }

        return object;
    }

    function clamp(number, min, max) {
        return Math.min(Math.max(number, min), max);
    }

    if (isFunction(forEachFN)) {
        $.forEach = function (array) {
            return forEachFN.apply(array, slice.call(arguments, 1));
        };
    } else {
        $.forEach = function (array, fn, thisArg) {
            var object = argToObject(array),
                length,
                index;

            throwIfNotFunction(fn);
            for (index = 0, length = clamp(object.length, 0, MAX_UINT32); index < length; index += 1) {
                if (hasOwn.call(object, index)) {
                    fn.call(thisArg, object[index], index, object);
                }
            }
        };
    }

    if (isFunction(filterFN)) {
        $.filter = function (array) {
            return filterFN.apply(array, slice.call(arguments, 1));
        };
    } else {
        $.filter = function (array, fn, thisArg) {
            var object = argToObject(array),
                next,
                length,
                arr,
                index,
                element;

            throwIfNotFunction(fn);
            for (arr = [], next = index = 0, length = clamp(object.length, 0, MAX_UINT32); index < length; index += 1) {
                if (hasOwn.call(object, index)) {
                    element = object[index];
                    if (fn.call(thisArg, element, index, object)) {
                        arr[next] = element;
                        next += 1;
                    }
                }
            }

            return arr;
        };
    }

    if ('getElementsByClassName' in document) {
        $.getElementsByClassName = function (elementNode, className) {
            return elementNode.getElementsByClassName(className);
        };
    } else {
        $.getElementsByClassName = function (elementNode, className) {
            return $.filter(elementNode.getElementsByTagName('*'), function (element) {
                return $.classList.contains(element, className) ? element : privateUndefined;
            });
        };
    }

    if (typeof module === 'object' && module && typeof module.exports === 'object' && module.exports) {
        module.exports = $;
    } else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
        define($);
    } else {
        window.$ = $;
    }
}());

Javascript - the actual code.

$.forEach($.getElementsByClassName(document, 'normal'), function (element) {
    $.addEventListener(element, 'click', function () {
        $.classList.toggle(element, 'change');
    });
});

On jsFiddle

5 Comments

It's not a good idea to just copy and paste a library as an answer. Looks good, but doesn't seem to support element.getElementsByClassName (vs document.gEBCN), but maybe I missed it. :-)
@RobG It's not a copy and paste of a library, ok it is of a kind, but parts of my own private stash/testing library. It is easy to support element.gEBCN (a very small modification to add an argument to accept a starting element), I thought I included that version but obviously not. The point was more to show the lengths that one has to go to if you want to support older environments and still have a syntax that resembles ECMA5 without polluting/modifying host objects. So that is what is required. Personally I would probably use es5 shim and also jQuery if I really really needed it. :)
The code that I provided (in part) is test/leadup code to a library that I'm working on. github.com/Xotic750/util-x :)
Hey, it's a good start, but I fear the OP will dismiss it out of hand because of it's size. Not all is required for this exercise. If you can replace +4,000 lines of jQuery with 100 lines of bespoke code (built from your own library of tried and tested functions), why not? See MyLibrary.
Sure it will be dismissed, I just want crappy browsers to become extinct. :) I see you have done a great deal (over a long period) of DOM work there, nice. Personally I have been avoiding DOM stuffs and concentrating on core ECMA compatibility stuffs. (it's just a hobby for me - amateur :) Unless someone offers me a job, lol)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.