Question: To summarize, for my program, I want to be able to detect the 't', and prevent the user from adding something with a quantity of '3t', '3t3', or anything of that matter.

In other words, if the quantity starts with a number, but has letters in it, it will still go through and be added, which is what I DON'T WANT.

Here's the code for where I add things. Is there any approach I should do differently?
--------------------------------------------------------------------------------------

```js
function addProduct(){

    var input = document.getElementById("productName").value;
    var input2 = document.getElementById("cost").value;
    var input3 = parseInt(document.getElementById("quantity").value);

    var s_input3 = input3.toString();
    var table = document.getElementsByTagName('table')[0];

    if (isNaN(input2) || input2<0) 
    {
      alert("not a number");
      return(-1);
    }

    if (isNaN(input3) || input3<0)
    {
      alert("not a number");
      return(-1);
    }

  // MY ATTEMPT OF DETECTING THE 't'

  for (i = 0; i < s_input3.length; i++)
  {
    console.log(s_input3.length)

    if(!(isNaN(s_input3[0])  && isNan(s_input3[i])))
    {
      alert("not a number")
      return(-3)
    }
  }

```

[![Visual Example of what's happening](https://i.stack.imgur.com/4M2mm.jpg)](https://i.stack.imgur.com/4M2mm.jpg)

Answer: 
There's no simple built-in way, but you can achieve almost anything with the [attributer](https://github.com/magjac/d3-graphviz#graphviz_attributer) like so:

```js
const px2pt = 3 / 4;

function attributer(datum, index, nodes) {
    var selection = d3.select(this);
    if (datum.tag == "svg") {
        var width = datum.attributes.width;
        var height = datum.attributes.height;
        w = datum.attributes.viewBox.split(" ")[2];
        h = datum.attributes.viewBox.split(" ")[3];
        var x = (width * px2pt - w / 2) / 2;
        var y = (height * px2pt - h / 2) / 2;
        selection
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", -x + " " + -y + " " + (width * px2pt) + " " + (height * px2pt));
        datum.attributes.width = width;
        datum.attributes.height = height;
        datum.attributes.viewBox = -x + " " + -y + " " + (width * px2pt) + " " + (height * px2pt);
    }
}

d3.select("#graph").graphviz()
    .width(300)
    .height(300)
    .fit(true)
    .scale(.5)
    .attributer(attributer)
    .renderDot('digraph  {a -> b}');
```

```html
<script src="//d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/@hpcc-js/wasm@0.3.11/dist/index.min.js"></script>
<script src="https://unpkg.com/d3-graphviz@3.0.5/build/d3-graphviz.js"></script>

<div id="graph" style="width: 300px; height: 300px; border: 1px solid black"></div>
```
We use the zoom behavior to do this.

Basically, what we do is that we wait for the graph to change (`.on('transitionEnd')`) and then recenter the graph and set the zoom level to 1.

For this, we grab our graph's SVG element and read its `viewBox` property.
This property is close to the size of our graph.

Then, we get the zoom behavior of our graph (the same that is used for zooming and panning with the mouse) and use its [`transform`](https://github.com/d3/d3-zoom#zoom_transform) function.

The `transform` function can be used to set the zoom level as well as the translation, i.e., the panning of our graph, by passing a transform object.

[`d3.zoomIdentity`](https://github.com/d3/d3-zoom#zoomIdentity) gives us a new transform object with `scale = 1, x = 0, y = 0`, and by calling `translate` on it we can specify an `x` and `y` value to translate to. By default, d3-graphviz has on a plain graph, according to my small-scale empirical study, an `x`-translate of `4` and a `y`-translate that corresponds to the `viewbox height - 4`.

This leads to the following code (which also uses a `transition` to make the zoom transform smooth):

```
// you can also use 'renderEnd' if you do not use animations
graphVisualization.on('transitionEnd', () => {
      // Some zoom examples: https://observablehq.com/@d3/programmatic-zoom

      const svg = d3.select('#graph-wrapper svg')
      const viewBox = svg.attr('viewBox').split(' ')
      // const graphWidth = +viewBox[2]
      const graphHeight = +viewBox[3]

      const transform = graphVisualization.zoomBehavior()?.transform

      // Define scale and translate
      // Resetting zoom to 1
      // +4 and -4 are used since they seem to be the default d3-graphviz offsets
      svg.transition('translateTransition').call(transform, d3.zoomIdentity.translate(4, graphHeight - 4))
    })

```