---
title: Bitovi Academy - Traversing Elements
description: Learn how to move from one element to the next and a bit of meta programming.
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us) 
    - [ Functional Utilities ](https://www.bitovi.com/academy/learn-dom-jquery/functional-utilities.html?hsLang=en-us)
    - [ Finding Elements ](https://www.bitovi.com/academy/learn-dom-jquery/finding-elements.html?hsLang=en-us)
    - [ Traversing Elements ](https://www.bitovi.com/academy/learn-dom-jquery/traversing-elements.html?hsLang=en-us)
    - [ Attributes and Properties ](https://www.bitovi.com/academy/learn-dom-jquery/attributes-and-properties.html?hsLang=en-us)
    - [ Layout ](https://www.bitovi.com/academy/learn-dom-jquery/layout.html?hsLang=en-us)
    - [ Events Part 1 ](https://www.bitovi.com/academy/learn-dom-jquery/events-part-1.html?hsLang=en-us)
    - [ Events Part 2 ](https://www.bitovi.com/academy/learn-dom-jquery/events-part-2.html?hsLang=en-us)
    - [ Fn Plugins ](https://www.bitovi.com/academy/learn-dom-jquery/fn-plugins.html?hsLang=en-us)
    - [ Tabs Widget ](https://www.bitovi.com/academy/learn-dom-jquery/tabs-widget.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Traversing Elements `page`

Learn how to move from one element to the next and a bit of meta programming.

## Overview

We will learn about:

- Creating next/prev/parent/children functions
- Re-factoring methods

## Video

## Slides

## Setup

Run the following example in CodePen:

```
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css" />
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<script type="module">
  (function () {
    $ = function (selector) {
      if (!(this instanceof $)) {
        return new $(selector);
      }
      var elements;
      if (typeof selector === "string") {
        elements = document.querySelectorAll(selector);
      } else if ($.isArrayLike(selector)) {
        elements = selector;
      }
      [].push.apply(this, elements);
    };

    $.extend = function (target, object) {
      for (var prop in object) {
        if (object.hasOwnProperty(prop)) {
          target[prop] = object[prop];
        }
      }
      return target;
    };

    // Static methods
    $.extend($, {
      isArray: function (obj) {
        return Object.prototype.toString.call(obj) === "[object Array]";
      },
      isArrayLike: function (obj) {
        return (
          obj &&
          typeof obj === "object" &&
          (obj.length === 0 ||
            (typeof obj.length === "number" &&
              obj.length > 0 &&
              obj.length - 1 in obj))
        );
      },
      each: function (collection, cb) {
        if ($.isArrayLike(collection)) {
          for (var i = 0; i < collection.length; i++) {
            if (cb.call(this, i, collection[i]) === false) {
              break;
            }
          }
        } else {
          for (var prop in collection) {
            if (collection.hasOwnProperty(prop)) {
              if (cb.call(this, prop, collection[prop]) === false) {
                break;
              }
            }
          }
        }
        return collection;
      },
      makeArray: function (arr) {
        if ($.isArray(arr)) {
          return arr;
        }
        var array = [];
        $.each(arr, function (i, item) {
          array[i] = item;
        });
        return array;
      },
      proxy: function (fn, context) {
        return function () {
          return fn.apply(context, arguments);
        };
      },
    });

    function makeSimpleGetterSetter(prop) {
      return function (value) {
        if (arguments.length) {
          return $.each(this, function (i, element) {
            element[prop] = value;
          });
        } else {
          return this[0][prop];
        }
      };
    }

    $.extend($.prototype, {
      html: makeSimpleGetterSetter("innerHTML"),
      val: makeSimpleGetterSetter("value"),
      text: makeSimpleGetterSetter("textContent"),
      find: function (selector) {
        var elements = [];
        $.each(this, function (i, element) {
          var result = element.querySelectorAll(selector);
          elements.push(...result);
          // Or elements.push.apply(elements, result);
        });
        return new $(elements);
      },
      parent: function () {},
      next: function () {},
      prev: function () {},
      children: function () {},
    });
  })();
</script>
```

Each exercise builds on the previous exercise. There is a completed solution at the end of this page.

## Exercise: `collection.parent() -> collection`

### The problem

[collection.parent](https://api.jquery.com/parent/) gets the parent of each element in the current set of matched elements.

```
<ul class="level-1">
  <li class="item-i">I</li>
  <li class="item-ii">
    II
    <ul class="level-2">
      <li class="item-a">A</li>
      <li class="item-b">
        B
        <ul class="level-3">
          <li class="item-1">1</li>
          <li class="item-2">2</li>
          <li class="item-3">3</li>
        </ul>
      </li>
      <li class="item-c">C</li>
    </ul>
  </li>
  <li class="item-iii">III</li>
</ul>
<script type="module">
  import "https://unpkg.com/jquery@3/dist/jquery.js";

  $("li.item-a").parent().css("border", "solid 1px red");
</script>
```

Click to see test code

```
QUnit.test('$.fn.parent', function(){
    var $lis = $('#qunit-fixture')
        .html('<ul><li/><li/></ul><ul><li/><li/></ul>')
        .find('li:first-child');

    equal($lis.length, 2, 'got 2 uls');
    equal($lis.parent().length, 2, 'got 2 lis');

});

```

### What you need to know

- The [parentNode](https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode) property returns an elements parent node.

### The solution

Click to see the solution

```
      parent: function() {
        var parents = [];
        $.each(this, function(i, element){
          parents.push(element.parentNode)
        });
        return $(parents);
      },
```

## Exercise: `collection.next() -> collection`

### The problem

[collection.next](https://api.jquery.com/next/) gets the immediately following sibling of each element in the set of matched elements.

```
<ul>
  <li>list item 1</li>
  <li>list item 2</li>
  <li class="third-item">list item 3</li>
  <li>list item 4</li>
  <li>list item 5</li>
</ul>
<script type="module">
  import "https://unpkg.com/jquery@3/dist/jquery.js";

  $("li.third-item").next().css("border", "solid 1px red");
</script>
```

Click to see test code

```
QUnit.test('$.fn.next', function(){
    var $lis = $('#qunit-fixture')
        .html('<ul> <li></li> <li></li> </ul> <ul> <li></li> <li></li> </ul>')
        .find('li:first-child');

    equal($lis.length, 2, 'got 2 lis');
    equal($lis.next().length, 2, 'got 2 lis');
    equal($lis.next().next().length, 0, 'got 0 lis');

});

```

### What you need to know

- [nextElementSibling](https://developer.mozilla.org/en-US/docs/Web/API/NonDocumentTypeChildNode/nextElementSibling) returns the element immediately following the specified one in its parent’s children list, or `null` if the specified element is the last one in the list.

### The solution

Click to see the solution

```
      next: function() {
        var nexts = [];
        $.each(this, function(i, element){
          if(element.nextElementSibling) {
            nexts.push( element.nextElementSibling );
          }
        });
        return $(nexts);
      },
```

## Exercise: `collection.prev() -> collection`

### The problem

[collection.prev](https://api.jquery.com/prev/) get the immediately preceding sibling of each element in the set of matched elements.

Click to see test code

```
QUnit.test('$.fn.prev', function(){
    var $lis = $('#qunit-fixture')
        .html('<ul><li/><li/></ul><ul><li/><li/></ul>')
        .find('li:last-child');

    equal($lis.length, 2, 'got 2 uls');
    equal($lis.prev().length, 2, 'got 2 lis');
    equal($lis.prev().prev().length, 0, 'got 2 lis');

});

```

### What you need to know

- [previousElementSibling](https://developer.mozilla.org/en-US/docs/Web/API/NonDocumentTypeChildNode/previousElementSibling) returns the Element immediately prior to the specified one in its parent’s children list, or `null` if the specified element is the first one in the list.

### The solution

Click to see the solution

```
      prev: function() {
        var prevs = [];
        $.each(this, function(i, element){
          if(element.previousElementSibling) {
            prevs.push( element.previousElementSibling );
          }
        });
        return $(prevs);
      },
```

## Exercise: `collection.children() -> collection`

### The problem

[collection.children](https://api.jquery.com/children/) get the children of each element in the set of matched elements.

```
<ul class="level-1">
  <li class="item-i">I</li>
  <li class="item-ii">
    II
    <ul class="level-2">
      <li class="item-a">A</li>
      <li class="item-b">
        B
        <ul class="level-3">
          <li class="item-1">1</li>
          <li class="item-2">2</li>
          <li class="item-3">3</li>
        </ul>
      </li>
      <li class="item-c">C</li>
    </ul>
  </li>
  <li class="item-iii">III</li>
</ul>
<script type="module">
  import "https://unpkg.com/jquery@3/dist/jquery.js";

  $("ul.level-2").children().css("border", "solid 1px red");
</script>
```

Click to see test code

```
QUnit.test('$.fn.children', function(){
    var $ul = $('#qunit-fixture')
        .html('<ul><li/><li/></ul><ul><li/><li/></ul>')
        .children();

    equal($ul.length, 2, 'got 2 uls');
    equal($ul.children().length, 4, 'got four lis');

});

```

### What you need to know

- [children](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/children) returns the child elements of the node upon which it was called.

### The solution

Click to see the solution

```
      children: function() {
        var children = [];
        $.each(this, function(i, element){
          children.push(...element.children );
        });
        return $(children);
      },
```

## Exercise: eliminate duplicate code in `.find`, `.parent`, `.next`, `.prev`, and `.children`.

### The problem

Use meta programming techniques to reduce the duplicate code in the `.find`, `.parent`, `.next`, `.prev`, and `.children` functions.

```
      find: function(selector) {
        var elements = [];
        $.each(this, function(i, element){
            var result = element.querySelectorAll(selector);
            elements.push(...result);
            // Or elements.push.apply(elements, result);
        })
        return new $(elements);
      },
      parent: function() {
        var parents = [];
        $.each(this, function(i, element){
          parents.push(element.parentNode)
        });
        return $(parents);
      },
      next: function() {
        var nexts = [];
        $.each(this, function(i, element){
          if(element.nextElementSibling) {
            nexts.push( element.nextElementSibling );
          }
        });
        return $(nexts);
      },
      prev: function() {
        var prevs = [];
        $.each(this, function(i, element){
          if(element.previousElementSibling) {
            prevs.push( element.previousElementSibling );
          }
        });
        return $(prevs);
      },
      children: function() {
        var children = [];
        $.each(this, function(i, element){
          children.push(...element.children );
        });
        return $(children);
      },
```

Make a `makeTraverser(traverse)` function that takes a `traverse` function and produces a function.

The `traverse` function should be called for each element in the source collection. When calling `traverse`, its `this` should be each element.

The `traverse` functions should be able to return `null` and `element` or an array-like object of `elements`.

### What you need to know

You know everything you need to know. You can do it!

### The solution

Click to see the solution

```
function makeTraverser(traverser) {
  return function () {
    var elements = [],
      args = arguments;
    $.each(this, function (i, element) {
      var els = traverser.apply(element, args);
      if ($.isArrayLike(els)) {
        elements.push.apply(elements, els);
      } else if (els) {
        elements.push(els);
      }
    });
    return $(elements);
  };
}
```

```
      find: makeTraverser(function(selector) {
        return this.querySelectorAll(selector);
      }),
      parent: makeTraverser(function() {
        return this.parentNode;
      }),
      next: makeTraverser(function() {
        return this.nextElementSibling;
      }),
      prev: makeTraverser(function() {
        return this.previousElementSibling;
      }),
      children: makeTraverser(function() {
        return this.children;
      })
```

## Completed Solution

Click to see completed solution

```
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css">
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<script type="module">
(function() {
  $ = function(selector) {
    if ( !(this instanceof $) ) {
      return new $(selector);
    }
    var elements;
    if (typeof selector === "string") {
      elements = document.querySelectorAll(selector);
    } else if ($.isArrayLike(selector)) {
      elements = selector;
    }
    [].push.apply(this, elements);
  };

  $.extend = function(target, object) {
    for (var prop in object) {
      if (object.hasOwnProperty(prop)) {
        target[prop] = object[prop];
      }
    }
    return target;
  };

  // Static methods
  $.extend($, {
    isArray: function(obj) {
      return Object.prototype.toString.call(obj) === "[object Array]";
    },
    isArrayLike: function(obj) {
      return obj &&
        typeof obj === "object" &&
        (   obj.length === 0 ||
            typeof obj.length === "number" &&
            obj.length > 0 &&
            obj.length - 1 in obj );

    },
    each: function(collection, cb) {
      if ($.isArrayLike(collection)) {
        for (var i = 0; i < collection.length; i++) {
          if (cb.call(this, i, collection[i]) === false) {
            break;
          }
        }
      } else {
        for (var prop in collection) {
          if (collection.hasOwnProperty(prop)) {
            if (cb.call(this, prop, collection[prop]) === false) {
              break;
            }
          }
        }
      }
      return collection;
    },
    makeArray: function(arr) {
      if ($.isArray(arr)) {
        return arr;
      }
      var array = [];
      $.each(arr, function(i, item) {
        array[i] = item;
      });
      return array;
    },
    proxy: function(fn, context) {
      return function() {
        return fn.apply(context, arguments);
      };
    }
  });

  function makeTraverser(traverser) {
    return function() {
      var elements = [], args = arguments;
      $.each(this, function(i, element) {
        var els = traverser.apply(element, args);
        if ($.isArrayLike(els)) {
          elements.push.apply(elements, els);
        } else if (els) {
          elements.push(els);
        }
      });
      return $(elements);
    };
  }

  $.extend($.prototype, {
      html: function(newHtml) {
        if(arguments.length) {
          return $.each(this, function(i, element) {
            element.innerHTML = newHtml;
          });
        } else {
          return this[0].innerHTML;
        }
      },
      val: function(newVal) {
        if(arguments.length) {
          return $.each(this, function(i, element) {
            element.value = newVal;
          });
        } else {
          return this[0].value;
        }
      },
      text: function(newText) {
        if (arguments.length) {
          return $.each(this, function(i, element) {
            element.textContent = newText;
          });
        } else {
          return this[0].textContent;
        }
      },
      find: makeTraverser(function(selector) {
        return this.querySelectorAll(selector);
      }),
      parent: makeTraverser(function() {
        return this.parentNode;
      }),
      next: makeTraverser(function() {
        return this.nextElementSibling;
      }),
      prev: makeTraverser(function() {
        return this.previousElementSibling;
      }),
      children: makeTraverser(function() {
        return this.children;
      })
  });

})();
</script>

```

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-dom-jquery/traversing-elements.html",
          "description": "Learn how to move from one element to the next and a bit of meta programming. 
"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```