0

As a challenge, I am trying to create a JavaScript selection engine i.e. a JavaScript function that will return DOM elements given a CSS selector.

I cant use document.querySelector/document.querySelectorAll.

I am currently creating a object of the parameter, but am now stuck. I now need to loop through every element on the page, and if it matches my tag, or class/id, push that element to an array.

$("div") //Should return 2 DIVs

$("img.some_class") //Should return 1 IMG

$("#some_id") //Should return 1 DIV 

$(".some_class") //Should return 1 DIV and 1 IMG

function $ (selector) { 

	var elements =[];

	var pageTags =[];

	var all = document.getElementsByTagName("*");

	//splits selector
	var arg = parse(selector);

	function parse(subselector) {
		 var obj = {tags:[], classes:[], ids:[], attrs:[]};
		 subselector.split(/(?=\.)|(?=#)|(?=\[)/).forEach(function(token){
		    switch (token[0]) {
		      case '#':
		         obj.ids.push(token.slice(1));
		        break;
		      case '.':
		         obj.classes.push(token.slice(1));
		        break;
		      case '[':
		         obj.attrs.push(token.slice(1,-1).split('='));
		        break;
		      default :
		         obj.tags.push(token);
		        break;
		    }
		  });
		  return obj;
	}

	console.log(arg);


	for (var item of all) {

		//gets tagname of all page elements
		var element = item.tagName.toLowerCase();

		console.log(element);
		//if argument contains DOM element
		if (arg.indexOf(element) !== -1) {

			var x = document.getElementsByTagName(element);
			
			for (var test of x) {
				elements.push(test);
			}

		}

	}

	return elements;

}
<html>
  <head>
    <script src="Answer.js"></script>
    <script src="Test.js"></script>
  </head>
  <body onload="test$()">
    <div></div>
    <div id="some_id" class="some_class some_other_class"></div>
    <img id="some_other_id" class="some_class some_other_class"></img>
    <input type="text">
  </body>
</html>

Please any help on how to do this will be appreciated.

7
  • 2
    Only for this one time I'd say "use jquery" Commented Dec 3, 2017 at 23:50
  • Challenge is to use vanilla JavaScript :( Commented Dec 3, 2017 at 23:58
  • You don't know or don't want to use querySelectorAll()? Commented Dec 4, 2017 at 0:00
  • That’s the challenge @Mamun, I can’t use it or jQuery. Commented Dec 4, 2017 at 0:09
  • This sounds like homework. It's homework isn't it? It's definitely homework. Commented Dec 4, 2017 at 0:13

1 Answer 1

1

check this jsfiddle. There would be many many more combinations of course ...

I limited the test cases to the html example you provided.

function _select(attrValues, tagFilter, cssSel) {

  var results = [];
  //var value = selector.slice(1);
  var all = document.getElementsByTagName(tagFilter);
  //look for an id attribute
  if (cssSel === '#') {
    for (var i = 0; i < all.length; i++) {
      if (all[i].id === attrValues) {
        results.push(all[i]);
      }
    }
  } else {
    if (typeof attrValues === 'string') {
      for (var i = 0; i < all.length; i++) {
        if (all[i].classList.contains(attrValues)) {
            results.push(all[i]);
        }
      }
    } else {
        //multiple selector classes
      var found = 0;
      for (var i = 0; i < all.length; i++) {
        for (var j = 0; j < attrValues.length; j++) {
          if (all[i].classList.contains(attrValues[j])) {
            found += 1;
            if (found === attrValues.length) {
              results.push(all[i]);
            }
          }
        }
      }
    }
  }
  return results;
}

function $(selector) {

  var cssSel = selector.charAt(0);
  var cssSelectors = ['.', '#'];

  if (cssSel === cssSelectors[0] || cssSel === cssSelectors[1]) {
    //direct selector
    var attrValue = selector.slice(1),
      tagFilter = '*';
    return _select(attrValue, tagFilter, cssSel)
  } else {
    for (var i = 0; i < cssSelectors.length; i++) {
      var tokens = selector.split(cssSelectors[i]);
      if (tokens.length > 1 && tokens[0] !== "") {
        //nested selector
        var tagFilter = tokens[0], //the first of the array should be the tagname ,because the case of the cssSelector at charAt(0) should have been caught in the if at the beginning.
          attrValue = tokens.slice(1); //the rest of the array are selector values
        return _select(attrValue, tagFilter, cssSel)
      }
    }
  }
  return document.getElementsByTagName(selector);
}

//TEST cases

var results = $("div")
console.log('Should return 2 DIVs')
for ( var e of results){
    console.log(e)
}

var results = $(".some_class")
console.log('Should return 1 DIV and 1 IMG')
for ( var e of results){
    console.log(e)
}

var results = $("#some_id")
console.log('Should return 1 DIV ')
for ( var e of results){
    console.log(e)
}

var results = $("img.some_class")
console.log('Should return 1 IMG')
for ( var e of results){
    console.log(e)
}

var results = $("div.some_class.some_other_class")
console.log('Should return 1 div')
for ( var e of results){
    console.log(e)
}
Sign up to request clarification or add additional context in comments.

Comments

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.