I am working on a billiards scorekeeping app implemented in HTML/CSS/javascript at this repo:
https://github.com/Zenilogix-Carl/Site
I have been developing using Windows and Chrome, getting expected outputs or at least able to diagnose issues in that environment. I’ve seen it run on some friends’ iPhones and I get very different (and flawed) results. I am hoping to find someone who can help me identify and correct the issues affecting iPhone.
The three files relevant to my issue are:
- NineBall.html
- Billiards.js
- styles.css
I’ve attached screenshots below. Notice that in Chrome, all balls are rendered with a drop-shadow; this is defined in the BilliardBall class and extended into BilliardBallWithState (both in Billiards.js) and, depending on current state, may be turned on or off by function updateBallState in NineBall.html. Initial state should show the drop-shadow for all balls, which it does in Chrome, but fails to on iPhone.
class BilliardBall {
constructor(number, size, allowForShadow) {
var colors = ["yellow", "blue", "red", "purple", "orange", "green", "brown", "var(--ballBlack)"];
var color = Number.isInteger(number) ? colors[(number - 1) % 8] : colors[0];
var isStripe = Number.isInteger(number) ? (((number - 1) / 8) >= 1) : false;
this.number = number;
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", allowForShadow ? "0 0 120 120" : "0 0 105 100");
svg.setAttribute("width", size);
svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
this.ballGraphic = g;
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
if (isStripe) {
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 48);
circle.setAttribute("style", "fill: white;");
g.appendChild(circle);
var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", "M 16 16 L 84 16 A 50 50 0 0 1 84 84 L 16 84 A 50 50 0 0 1 16 16 ");
path.setAttribute("style", "fill: " + color + "; stroke-width: 1; stroke: grey;");
g.appendChild(path);
circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 48);
circle.setAttribute("style", "fill: transparent; stroke-width: 1; stroke: var(--ballOutline);");
g.appendChild(circle);
} else {
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 48);
circle.setAttribute("style", `fill: ${color}; stroke-width: 1; stroke: var(--ballOutline);`);
g.appendChild(circle);
}
circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 27);
circle.setAttribute("style", "fill: white; stroke-width: 1; stroke: grey;");
g.appendChild(circle);
var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", 50);
text.setAttribute("y", 53);
text.setAttribute("text-anchor", "middle");
text.setAttribute("dominant-baseline", "middle");
text.setAttribute("font-weight", "bold");
text.setAttribute("font-size", number > 9 ? "32px" : "40px");
text.innerHTML = number;
g.appendChild(text);
svg.appendChild(g);
this.element = svg;
}
}
class BilliardBallWithState extends BilliardBall {
constructor(number, size, clickFn, foulText) {
super(number, size, true);
const svg = this.element;
svg.onclick = function () { clickFn(number)};
let text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("style", "visibility: hidden;");
text.classList.add("dropShadow");
text.setAttribute("x", 50);
text.setAttribute("y", 50);
text.setAttribute("text-anchor", "middle");
text.setAttribute("dominant-baseline", "middle");
text.setAttribute("font-size", "30px");
text.setAttribute("fill", "red");
text.setAttribute("stroke-width", "1");
text.setAttribute("stroke", "white");
text.innerHTML = foulText;
svg.appendChild(text);
this.foulText = text;
text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("style", "visibility: hidden;");
text.classList.add("dropShadow");
text.setAttribute("x", 40);
text.setAttribute("y", 90);
text.setAttribute("font-size", "80px");
text.setAttribute("fill", "green");
text.setAttribute("stroke-width", "1");
text.setAttribute("stroke", "white");
text.innerHTML = "✔";
svg.appendChild(text);
this.checkMark = text;
this.showNormal();
}
dimElement(elem, dim) {
if (dim) {
elem.classList.remove("dropShadow");
elem.classList.add("dimmed");
} else {
elem.classList.add("dropShadow");
elem.classList.remove("dimmed");
}
}
showElement(elem, show) {
elem.style.visibility = show ? "visible" : "hidden";
}
showNormal() {
this.dimElement(this.ballGraphic, false);
this.showElement(this.foulText, false);
this.showElement(this.checkMark, false);
}
showPocketed(checked) {
this.dimElement(this.ballGraphic, true);
this.showElement(this.foulText, false);
this.showElement(this.checkMark, checked);
}
showFoul() {
this.dimElement(this.ballGraphic, true);
this.showElement(this.foulText, true);
this.showElement(this.checkMark, false);
}
}
function updateBallState(number) {
const currentBallState = match.ballStates[number - 1];
const ball = balls[number];
switch (currentBallState) {
case "normal":
ball.showNormal();
break;
case "dead":
ball.showFoul();
break;
default:
ball.showPocketed(currentBallState === "won");
break;
}
}
Also, although not reflecting in the screen caps, it seems that cookies are not working correctly on iPhone, so I am wondering if I am handling them correctly – see onLoad function in NineBall.html and Preferences class in Billiards.js.
I need to understand what I am doing that is not functional/uniformly supported across browsers and what I need to do to fix it.
Chrome screen-cap (expected appearance):
iPhone screen-cap (shows anomaly as described above):


