'Browser tab badge notification

I have been searching how to get a notification badge on the tab of the website page like in the image below, but couldn't find much, or anything that works. I have made a chat site and want people to know if there is a new notification in the channel they are in. But I just need to know how I can get a red badge button on the site favicon or something like that.

Preferably something like how Discord has done:

enter image description here

I have tried this, but doesn't seem to work:

var count = 0;

var title = document.title;

function changeTitle() {
    count++;
    var newTitle = '(' + count + ') ' + title;
    document.title = newTitle;
}

function newUpdate() {
    update = setInterval(changeTitle, 2000);
}
var docBody = document.getElementById('site-body');
docBody.onload = newUpdate;


Solution 1:[1]

This should get you started.
Tl;Dr: Update the <link> element with a new icon image.

enter image description here

Basically what it does:

  • takes the source of the image from the <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /> element
  • creates an in-memory Canvas element
  • applies the favicon image
  • draws the shape on top
  • draws the text value on top
  • updates the <link> element href attribute from the final composite of the canvas element imageData

Use like:

const myBadgerOptions = {}; // See: constructor for customization options
const myBadger = new Badger(myBadgerOptions);

// Live update value example:
myBadger.value = 3;

// Remove badge example:
// myBadger.value = 0;

// If needed, get the generated base64 image data: 
// console.log(myBadger.dataURL);

Badger constructor:

/**
 * Add notification badge (pill) to favicon in browser tab
 * @url stackoverflow.com/questions/65719387/
 */
class Badger {
  constructor(options) {
    Object.assign(
      this, {
        backgroundColor: "#f00",
        color: "#fff",
        size: 0.6,      // 0..1 (Scale in respect to the favicon image size)
        position: "ne", // Position inside favicon "n", "e", "s", "w", "ne", "nw", "se", "sw"
        radius: 8,      // Border radius
        src: "",        // Favicon source (dafaults to the <link> icon href)
        onChange() {},
      },
      options
    );
    this.canvas = document.createElement("canvas");
    this.src = this.src || this.faviconEL.getAttribute("href");
    this.ctx = this.canvas.getContext("2d");
  }

  faviconEL = document.querySelector("link[rel$=icon]");

  _drawIcon() {
    this.ctx.clearRect(0, 0, this.faviconSize, this.faviconSize);
    this.ctx.drawImage(this.img, 0, 0, this.faviconSize, this.faviconSize);
  }

  _drawShape() {
    const r = this.radius;
    const xa = this.offset.x;
    const ya = this.offset.y;
    const xb = this.offset.x + this.badgeSize;
    const yb = this.offset.y + this.badgeSize;
    this.ctx.beginPath();
    this.ctx.moveTo(xb - r, ya);
    this.ctx.quadraticCurveTo(xb, ya, xb, ya + r);
    this.ctx.lineTo(xb, yb - r);
    this.ctx.quadraticCurveTo(xb, yb, xb - r, yb);
    this.ctx.lineTo(xa + r, yb);
    this.ctx.quadraticCurveTo(xa, yb, xa, yb - r);
    this.ctx.lineTo(xa, ya + r);
    this.ctx.quadraticCurveTo(xa, ya, xa + r, ya);
    this.ctx.fillStyle = this.backgroundColor;
    this.ctx.fill();
    this.ctx.closePath();
  }

  _drawVal() {
    const margin = (this.badgeSize * 0.18) / 2;
    this.ctx.beginPath();
    this.ctx.textBaseline = "middle";
    this.ctx.textAlign = "center";
    this.ctx.font = `bold ${this.badgeSize * 0.82}px Arial`;
    this.ctx.fillStyle = this.color;
    this.ctx.fillText(this.value, this.badgeSize / 2 + this.offset.x, this.badgeSize / 2 + this.offset.y + margin);
    this.ctx.closePath();
  }

  _drawFavicon() {
    this.faviconEL.setAttribute("href", this.dataURL);
  }

  _draw() {
    this._drawIcon();
    if (this.value) this._drawShape();
    if (this.value) this._drawVal();
    this._drawFavicon();
  }

  _setup() {
    this.faviconSize = this.img.naturalWidth;
    this.badgeSize = this.faviconSize * this.size;
    this.canvas.width = this.faviconSize;
    this.canvas.height = this.faviconSize;
    const sd = this.faviconSize - this.badgeSize;
    const sd2 = sd / 2;
    this.offset = {
      n:  {x: sd2, y: 0 },
      e:  {x: sd, y: sd2},
      s:  {x: sd2, y: sd},
      w:  {x: 0, y: sd2},
      nw: {x: 0, y: 0},
      ne: {x: sd, y: 0},
      sw: {x: 0, y: sd},
      se: {x: sd, y: sd},
    }[this.position];
  }

  // Public functions / methods:

  update() {
    this._value = Math.min(99, parseInt(this._value, 10));
    if (this.img) {
      this._draw();
      if (this.onChange) this.onChange.call(this);
    } else {
      this.img = new Image();
      this.img.addEventListener("load", () => {
        this._setup();
        this._draw();
        if (this.onChange) this.onChange.call(this);
      });
      this.img.src = this.src;
    }
  }

  get dataURL() {
    return this.canvas.toDataURL();
  }

  get value() {
    return this._value;
  }

  set value(val) {
    this._value = val;
    this.update();
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1