---
title: Change Survey HTML with JavaScript
product: Form Library
description: SurveyJS Form Library allows you to modify HTML markup of individual survey elements to extend their default functionality and have greater flexibility and control over their layout and positioning. This free demo for JavaScript shows how to add tooltips to question titles. Hover over a question title to see the infotip appear.
framework: Angular
source: https://surveyjs.io/form-library/examples/change-survey-html-with-javascript/angular
index: https://surveyjs.io/form-library/examples/overview.md
---

# Change Survey HTML with JavaScript (Angular)

SurveyJS Form Library offers multiple customization options for survey elements (panels and questions):

- Configure templates for answer options in single- and multi-select questions ([View Demo](https://surveyjs.io/form-library/examples/add-custom-items-to-single-and-multi-select-questions/))
- Implement custom question renderers based on built-in question types ([View Demo](https://surveyjs.io/form-library/examples/create-custom-question-renderer/))
- Create custom question types ([View Demo](https://surveyjs.io/form-library/examples/add-custom-question-type-to-form-builder/))

If none of these options meets your requirements, you can handle events to modify HTML markup of individual survey elements. The [`SurveyModel`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model) class raises the following events for this purpose:

- [`onAfterRenderQuestion`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#onAfterRenderQuestion)
- [`onAfterRenderQuestionInput`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#onAfterRenderQuestionInput)
- [`onAfterRenderMatrixCell`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#onAfterRenderMatrixCell)
- [`onAfterRenderPanel`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#onAfterRenderPanel)
- [`onAfterRenderPage`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#onAfterRenderPage)
- [`onAfterRenderSurvey`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#onAfterRenderSurvey)

Each of these events accepts the `options.htmlElement` parameter, which contains an <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement" target="_blank"><code>HTMLElement</code></a> object. You can use its properties and methods to change HTML markup in your JavaScript code. For example, this demo shows how to add tooltips to question titles. Tooltip texts are specified in a question's [`title`](https://surveyjs.io/form-library/documentation/api-reference/text-entry-question-model#title) property and are separated from the title text by a special character combination (`"||"`). The `onAfterRenderQuestion` event handler splits the `title` property value and assigns title text to `HTMLElement`'s <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText" target="_blank"><code>innerText</code></a> property and the tooltip text to `HTMLElement`'s <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/title" target="_blank"><code>title</code></a> property. Hover the mouse pointer over a question title to see its tooltip.

## Files

### `src/app/components/json.ts`

```ts
export const json = { 
  "elements": [
    {
      "type": "checkbox",
      "name": "os",
      "title": "What operating system(s) do you use on your work computer(s)?||Please select all that apply.",
      "description": "Hover the mouse pointer over the title above to see a tooltip.",
      "showOtherItem": true,
      "otherText": "Other (specify)",
      "choices": [ "Windows", "Linux", "Macintosh OSX" ]
    }, 
    {
      "type": "checkbox",
      "name": "languages",
      "title": "What programming language(s) do you use at work?||Please select all that apply but no more than three languages.",
      "description": "Hover the mouse pointer over the title above to see a tooltip.",
      "colCount": 4,
      "showOtherItem": true,
      "otherText": "Other (specify)",
      "choices": [
        "JavaScript", "Java", "Python", "CSS",
        "PHP", "Ruby", "C++", "C",
        "Shell", "C#", "Objective-C", "R",
        "VimL", "Go", "Perl", "CoffeeScript",
        "TeX", "Swift", "Scala", "Emacs Lisp",
        "Haskell", "Lua", "Clojure", "MATLAB",
        "Arduino", "Makefile", "Groovy", "Puppet",
        "Rust", "PowerShell"
      ],
      "maxSelectedChoices": 3
    }
  ]
};
```

### `src/app/components/survey.component.css`

```css
/* You can add your custom CSS here. */
```

### `src/app/components/survey.component.html`

```html
<survey [model]="model" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; height:100%"></survey>
```

### `src/app/components/survey.component.ts`

```ts
import { Component, OnInit } from "@angular/core";
import { Model } from "survey-core";
import { json } from "./json";
import "./survey.component.css";
import "survey-core/survey-core.min.css";

const titleDivider = "||";
function updateStringComponents(_, options) {
    options.htmlElement.querySelectorAll('.sv-string-viewer').forEach((el) => {
        const text = el.innerText;
        if (text.indexOf(titleDivider) > -1) {
            const strings = text.split(titleDivider);
            el.title = strings[1];
            el.innerText = strings[0];
        }
    });
}
@Component({
    // tslint:disable-next-line:component-selector
    selector: "component-survey",
    templateUrl: "./survey.component.html",
    styleUrls: ["./survey.component.css"]
})
export class SurveyComponent implements OnInit {
    model: Model;
    ngOnInit() {
        const survey = new Model(json);
        survey.onAfterRenderQuestion.add(updateStringComponents);
        survey.onComplete.add((sender, options) => {
            console.log(JSON.stringify(sender.data, null, 3));
        });
        this.model = survey;
    }
}
```

### `src/app/components/theme.ts`

```ts
export const themeJson = {};
```

### `src/app/app.component.css`

```css

```

### `src/app/app.component.html`

```html
<component-survey></component-survey>
```

### `src/app/app.component.ts`

```ts
import { Component } from "@angular/core";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"]
})
export class AppComponent {
    title = "CodeSandbox";
}
```

### `src/app/app.module.ts`

```ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

import { AppComponent } from "./app.component";
import { SurveyModule } from "survey-angular-ui";
import { SurveyComponent } from "./components/survey.component";

@NgModule({
    declarations: [AppComponent, SurveyComponent],
    imports: [BrowserModule, SurveyModule],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }
```

### `src/environments/environment.prod.ts`

```ts
export const environment = {
    production: true
};
```

### `src/environments/environment.ts`

```ts
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.

export const environment = {
    production: false
};
```

### `src/index.html`

```html
<app-root></app-root>
```

### `src/main.ts`

```ts
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(err => console.log(err));
```

### `src/polyfills.ts`

```ts
/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install classlist.js`.

/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';

/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import "core-js/proposals/reflect-metadata";

/**
 * Required to support Web Animations `@angular/platform-browser/animations`.
 * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
 **/
// import 'web-animations-js';  // Run `npm install web-animations-js`.

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import "zone.js/dist/zone"; // Included with Angular CLI.

/***************************************************************************************************
 * APPLICATION IMPORTS
 */
```

### `src/styles.css`

```css
/* You can add your custom CSS here. */
```

### `src/typings.d.ts`

```ts
/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
    id: string;
}
```

### `.angular-cli.json`

```json
{
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [ "assets", "favicon.ico" ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "prefix": "app",
      "styles": [ "styles.css" ],
      "scripts": [ ],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ]
}
```

### `_tsconfig.json`

```json
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "resolveJsonModule": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ]
  }
}
```

### `package.json`

```json
{
  "name": "surveyjs-angular",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --prod",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "14.1.1",
    "@angular/cdk": "14.1.1",
    "@angular/common": "14.1.1",
    "@angular/compiler": "14.1.1",
    "@angular/core": "14.1.1",
    "@angular/forms": "14.1.1",
    "@angular/platform-browser": "14.1.1",
    "@angular/platform-browser-dynamic": "14.1.1",
    "@angular/router": "14.1.1",
    "core-js": "3.6.4",
    "rxjs": "6.5.4",
    "survey-core": "latest",
    "survey-angular-ui": "latest",
    "tslib": "1.13.0",
    "zone.js": "0.11.7"
  },
  "devDependencies": {
    "@angular/cli": "1.6.6",
    "@angular/compiler-cli": "^5.2.0",
    "@angular/language-service": "^5.2.0",
    "@types/core-js": "0.9.46",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~4.1.0",
    "tslint": "~5.9.1",
    "typescript": "3.9.7"
  },
  "keywords": [ "angular", "surveyjs" ],
  "description": "SurveyJS-Angular example project"
}
```

## Other Frameworks

- [React](https://surveyjs.io/form-library/examples/change-survey-html-with-javascript/reactjs.md)
- [Vue 3](https://surveyjs.io/form-library/examples/change-survey-html-with-javascript/vue3js.md)
- [jQuery](https://surveyjs.io/form-library/examples/change-survey-html-with-javascript/jquery.md)
- [Vanilla JS](https://surveyjs.io/form-library/examples/change-survey-html-with-javascript/vanillajs.md)
