Let's build browser engine! in typescript vol7 Selector matching

sanemat

Murahashi [Matt] Kenichi

Posted on May 10, 2019

 Let's build browser engine! in typescript vol7 Selector matching

for selector matching, I create some shortcut.

test("element id found", () => {
  expect(new ElementData("no mean", new Map([["id", "target"]])).id()).toEqual("target");
});

test("element id not found", () => {
  expect(new ElementData("no mean", new Map([["no mean", "no mean"]])).id()).toBeNull();
});
Enter fullscreen mode Exit fullscreen mode
export class ElementData {
  (snip)

  id(): string | null {
    return this.attributes.get("id") || null;
  }
}
Enter fullscreen mode Exit fullscreen mode
test("element class 1 found", () => {
  expect(new ElementData("no mean", new Map([["class", "target1"]])).classes()).toEqual(
    new Set(["target1"])
  );
});

test("element class 2 found", () => {
  expect(new ElementData("no mean", new Map([["class", "target1 target2"]])).classes()).toEqual(
    new Set(["target1", "target2"])
  );
});

test("element class 0 found", () => {
  expect(new ElementData("no mean", new Map([])).classes()).toEqual(new Set([]));
});
Enter fullscreen mode Exit fullscreen mode
export class ElementData {
  (snip)

  classes(): Set<string> {
    const classes = this.attributes.get("class");
    if (!classes) {
      return new Set([]);
    }
    return new Set(classes.split(" "));
  }
}
Enter fullscreen mode Exit fullscreen mode

selector matching. using this simple selector.

test("matches simple selector", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("no mean", new Map([])),
      new SimpleSelector(null, null, [])
    )
  ).toBe(true);
});

test("matches simple selector different tag", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("other", new Map([])),
      new SimpleSelector("some", null, [])
    )
  ).toBe(false);
});

test("matches simple selector same tag", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("target", new Map([])),
      new SimpleSelector("target", null, [])
    )
  ).toBe(true);
});

test("matches simple selector different id", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("no mean1", new Map([["id", "other"]])),
      new SimpleSelector(null, "some", [])
    )
  ).toBe(false);
});

test("matches simple selector same id", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("no mean1", new Map([["id", "target"]])),
      new SimpleSelector(null, "target", [])
    )
  ).toBe(true);
});

test("matches simple selector different class", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("no mean1", new Map([["class", "other1 other2"]])),
      new SimpleSelector(null, null, ["some1", "some2"])
    )
  ).toBe(false);
});

test("matches simple selector same class", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("no mean1", new Map([["class", "target1 other2"]])),
      new SimpleSelector(null, null, ["target1", "some2"])
    )
  ).toBe(true);
});

test("matches simple selector same tag, other id", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("same", new Map([["id", "other"]])),
      new SimpleSelector("same", "some", [])
    )
  ).toBe(false);
});

test("matches simple selector same tag, same id, different class", () => {
  expect(
    matchesSimpleSelector(
      new ElementData("same", new Map([["id", "same"], ["class", "other1 other2"]])),
      new SimpleSelector("same", "same", ["some1", "some2"])
    )
  ).toBe(false);
});
Enter fullscreen mode Exit fullscreen mode
// returns true if there are no none-match
export function matchesSimpleSelector(elem: ElementData, selector: SimpleSelector): boolean {
  const tagName = selector.tagName;
  if (tagName && tagName !== elem.tagName) {
    return false;
  }

  const id = selector.id;
  if (id && id !== elem.id()) {
    return false;
  }

  const classes = selector.classValue;
  if (classes.length !== 0) {
    let included = false;
    for (let className of classes) {
      if (elem.classes().has(className)) {
        included = true;
        break;
      }
    }
    if (!included) {
      return false;
    }
  }

  return true;
}
Enter fullscreen mode Exit fullscreen mode
test("matches no none-match", () => {
  expect(
    matches(
      new ElementData("no mean", new Map([])),
      new Selector.Simple(new SimpleSelector(null, null, []))
    )
  ).toBe(true);
});

test("matches none-match", () => {
  expect(
    matches(
      new ElementData("no mean", new Map([])),
      new Selector.Simple(new SimpleSelector(null, "some", []))
    )
  ).toBe(false);
});

test("matches match", () => {
  expect(
    matches(
      new ElementData("no mean", new Map([["id", "target"]])),
      new Selector.Simple(new SimpleSelector(null, "target", []))
    )
  ).toBe(true);
});
Enter fullscreen mode Exit fullscreen mode
export function matches(elem: ElementData, selector: Selector): boolean {
  switch (selector.format) {
    case Selector.Format.Simple:
      return matchesSimpleSelector(elem, selector.selector);
  }
}
Enter fullscreen mode Exit fullscreen mode

references

series

💖 💪 🙅 🚩
sanemat
Murahashi [Matt] Kenichi

Posted on May 10, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related