Skip to content

Government debt at 88.5% of GDP in euro area

[]

Printing can have negative environmental impacts due to paper waste. We encourage you to explore eco-friendly alternatives. Bookmarking this page in your browser allows you to access it anytime.

This publication can also be converted to PDF, if you have a PDF printer installed on your computer. PDF printers function as virtual printers, enabling you to create digital copies of web pages and access the content offline.

{ const buttonPrintPage = document.getElementById(‘estat-print-page-modal-toggle’); const cookieSkipModal = ‘skipModalBeforePrinting’; if (configuration.displayInPageHeader) { const headerElement = document.querySelector(‘.ecl-page-header .ecl-page-header__meta’) || document.querySelector(‘.ecl-page-header .ecl-page-header__title-container ‘) if (headerElement) { headerElement.prepend(buttonPrintPage); headerElement.classList.add(‘ecl-u-width-100’) } } buttonPrintPage.addEventListener(‘click’, () => { const skipModal = readCookie(cookieSkipModal); if (skipModal == ‘true’) { window.print(); } else { const eclModalElement = document.getElementById(‘estat-print-page-modal’); const eclModal = ECL.components.get(eclModalElement); eclModal.openModal(); } }); document.getElementById(‘print-submit’).addEventListener(‘click’, () => { const skipModal = document.getElementById(‘checkbox-skip-modal’).checked if (skipModal) { writeCookieWithMaxAgeValue(cookieSkipModal, ‘true’, 15768000); } else { deleteCookie(cookieSkipModal); } window.print(); }); });;}());]]>

82.1% of GDP in EU

Overview

At the end of the third quarter of 2025, the general government gross debt to GDP ratio in the euro area (EA20) stood at 88.5%, an increase compared with 88.2% at the end of the second quarter of 2025. In the EU, the ratio also increased from 81.9% to 82.1%.

Compared with the third quarter of 2024, the government debt to GDP ratio increased in both the euro area (from 87.7% to 88.5%) and the EU (from 81.3% to 82.1%).

At the end of the third quarter of 2025, the general governmentdebt was made up of 84.2% debt securities in the euro area and 83.6% in the EU, 13.3% loans in the euro area and 13.9% in the EU and 2.6% currency and deposits in the euro area and 2.5% in the EU.

Due to the involvement of EU Member States’ governments in lending to certain Member States, quarterly data on intergovernmental lending (IGL) are also published. The IGL as percentage of GDP at the end of the third quarter of 2025 stood at 1.4% in the euro area and at 1.2% in the EU.

These data are released by Eurostat, the statistical office of the European Union.

Euro area and EU general government gross debt

2024Q3

2025Q2p

2025Q3p

Euro area 20

(million euro)

13 228 782

13 676 742

13 854 637

(% of GDP)

87.7

88.2

88.5

(million euro)

337 593

348 052

353 512

(% of total debt)

2.6

2.5

2.6

(million euro)

11 119 788

11 518 784

11 661 087

(% of total debt)

84.1

84.2

84.2

(million euro)

1 771 402

1 809 906

1 840 037

(% of total debt)

13.4

13.2

13.3

(million euro)

224 314

216 518

219 845

(% of GDP)

1.5

1.4

1.4

 

 

 

(million euro)

13 253 694

13 705 122

13 886 456

(% of GDP)

87.3

87.7

88.1

(million euro)

337 593

348 052

353 512

(% of total debt)

2.5

2.5

2.5

(million euro)

11 141 118

11 543 685

11 689 229

(% of total debt)

84.1

84.2

84.2

(million euro)

1 774 983

1 813 384

1 843 714

(% of total debt)

13.4

13.2

13.3

(million euro)

224 314

216 518

219 845

(% of GDP)

1.5

1.4

1.4

EU

 

 

 

(million euro)

14 476 738

15 047 553

15 253 172

(% of GDP)

81.3

81.9

82.1

(million euro)

364 338

374 924

383 938

(% of total debt)

2.5

2.5

2.5

(million euro)

12 101 484

12 591 774

12 752 185

(% of total debt)

83.6

83.7

83.6

(million euro)

2 010 917

2 080 854

2 117 049

(% of total debt)

13.9

13.8

13.9

(million euro)

224 314

216 518

219 845

(% of GDP)

1.3

1.2

1.2

Government debt at the end of the third quarter of 2025 by Member State

The highest ratios of government debt to GDP at the end of the third quarter of 2025 were recorded in Greece (149.7%), Italy (137.8%), France (117.7%), Belgium (107.1%) and Spain (103.2%), and the lowest ratios were recorded in Estonia (22.9%), Luxembourg (27.9%), Bulgaria (28.4%), and Denmark (29.7%).

General government gross debt to GDP ratio, 2025Q3

Compared with the second quarter of 2025, eleven Member States registered an increase in their debt to GDP ratio at the end of the third quarter of 2025 and sixteen registered a decrease. The largest increases in the ratio were observed in Luxembourg (+2.6 percentage points – pp), Bulgaria (+2.1 pp), France (+1.8 pp), Lithuania and Romania (both +1.6 pp), and Austria (+1.5 pp). The largest decreases were recorded in Latvia (-2.9 pp), Greece (-2.2 pp), Slovenia and Finland (both -1.7 pp).

Changes in general government gross debt to GDP ratio, 2025Q3 compared to 2025Q2

Compared with the third quarter of 2024, sixteen Member States registered an increase in their debt to GDP ratio at the end of the third quarter of 2025 and eleven Member States registered a decrease. The largest increases in the ratio were recorded in Romania (+5.5 pp), Poland (+5.0 pp), Finland (+4.7 pp), Bulgaria (+4.1 pp) and France (+4.0 pp). The largest decreases were observed in Greece (-8.9 pp), Ireland (-7.1 pp), Cyprus (-6.1 pp), Denmark (-3.1 pp) and Latvia (-2.3 pp).

Changes in general government gross debt to GDP ratio, 2025Q3 compared to 2024Q3

Tables

General government gross debt by Member State

General government gross debt

Millions of national currency

% of GDP

Difference in pp of GDP 2025Q3p compared with:

2024Q3

2025Q2p

2025Q3p

2024Q3

2025Q2p

2025Q3p

2024Q3

2025Q2p

EUR

13 228 782

13 676 742

13 854 637

87.7

88.2

88.5

0.8

0.3

EUR

13 253 694

13 705 122

13 886 456

87.3

87.7

88.1

0.8

0.3

EUR

14 476 738

15 047 553

15 253 172

81.3

81.9

82.1

0.9

0.3

EUR

644 385

670 703

681 157

104.8

106.2

107.1

2.3

0.8

EUR*

24 918

28 386

31 825

24.3

26.3

28.4

4.1

2.1

CZK

3 449 070

3 627 212

3 623 901

43.4

43.8

43.1

-0.3

-0.7

DKK

932 969

888 596

900 757

32.7

29.6

29.7

-3.1

0.1

EUR

2 671 637

2 733 365

2 787 631

62.0

62.3

63.0

1.0

0.7

EUR

9 386

9 461

9 410

23.8

23.2

22.9

-1.0

-0.3

EUR

215 943

207 243

208 960

40.0

33.4

32.8

-7.1

-0.5

EUR

370 820

368 609

367 852

158.6

151.9

149.7

-8.9

-2.2

EUR

1 635 731

1 690 922

1 709 330

104.2

103.5

103.2

-1.1

-0.3

EUR

3 301 444

3 416 302

3 482 180

113.7

115.9

117.7

4.0

1.8

EUR

49 968

50 932

51 838

59.2

57.5

57.2

-1.9

-0.3

EUR

2 962 913

3 071 278

3 080 915

135.6

138.3

137.8

2.2

-0.5

EUR

22 886

21 774

21 748

66.7

61.4

60.6

-6.1

-0.7

EUR

18 853

19 776

18 965

47.4

48.0

45.2

-2.3

-2.9

EUR

29 521

31 815

33 635

38.0

39.1

40.7

2.6

1.6

EUR

21 790

21 870

24 451

25.6

25.2

27.9

2.2

2.6

HUF

60 605 593

63 986 465

64 146 655

76.1

76.2

75.2

-0.8

-0.9

EUR

10 165

11 110

11 215

44.8

46.8

46.5

1.7

-0.2

EUR

469 678

491 700

493 628

42.6

42.7

42.4

-0.3

-0.4

EUR

398 538

412 280

423 892

81.6

82.2

83.7

2.1

1.5

PLN

1 897 487

2 186 171

2 221 829

53.2

58.1

58.1

5.0

0.1

EUR

272 323

287 133

294 420

95.9

96.7

97.6

1.7

0.9

RON

916 404

1 039 857

1 096 295

53.3

57.3

58.9

5.5

1.6

EUR

44 245

47 494

46 930

66.3

69.3

67.6

1.3

-1.7

EUR

77 425

83 484

83 916

60.1

62.9

62.3

2.2

-0.6

EUR

225 432

245 999

242 401

82.2

88.5

86.8

4.7

-1.7

SEK

2 040 847

2 191 695

2 174 022

32.3

33.9

33.3

1.0

-0.6

NOK

2 191 307

2 303 056

2 315 139

40.9

42.0

42.0

1.1

0.0

General government gross debt by Member State

Components of general government gross debt, in % of GDP

IGL (assets)

Currency and deposits

Debt Securities

Loans

% of GDP

2025Q3p

2025Q3p

2025Q3p

2025Q3p

2.3

74.5

11.8

1.4

2.2

74.1

11.7

1.4

2.1

68.7

11.4

1.2

0.4

92.1

14.6

1.3

25.1

3.3

0.0

0.4

39.1

3.6

0.0

0.6

27.0

2.1

0.0

0.4

50.2

12.4

1.4

0.3

13.1

9.5

1.2

4.0

22.1

6.8

0.0

3.1

40.0

106.7

0.0

0.3

91.6

11.2

1.7

1.5

105.6

10.6

1.6

0.1

42.2

15.0

0.0

8.6

115.2

14.0

1.9

0.5

35.5

24.7

0.9

1.4

40.0

3.7

0.0

0.2

33.8

6.7

0.0

0.4

23.7

3.8

0.7

0.8

66.1

8.3

0.0

1.7

40.4

4.5

0.9

0.2

35.8

6.3

1.1

0.4

74.3

9.0

1.7

0.3

43.1

14.8

0.0

16.7

57.8

23.2

0.2

1.2

48.0

9.6

0.0

0.3

56.9

10.4

1.6

0.1

55.8

6.4

1.6

0.4

65.6

20.9

1.5

3.0

19.0

11.3

0.0

16.1

26.0

Notes for users

Methods and definitions

Quarterly data on government debt are collected from the Member States according to European System of Accounts (ESA 2010), see Annex B, ESA 2010 transmission programme, and refer to the Maastricht debt definition, used in the context of the Excessive Deficit Procedure (EDP). Annual EDP data, next to be notified in April 2026, are the subject of a thorough verification by Eurostat.

The general government gross debt is defined as the consolidated gross debt of the whole general government sector outstanding at the end of the quarter (at face value). General government debt consists of liabilities of general government in the following financial instruments: currency and deposits (AF.2), debt securities (AF.3) and loans (AF.4), as defined in ESA 2010.

The debt to GDP ratio is calculated for each quarter using the sum of quarterly GDP for the four last quarters. Quarterly data on GDP are the most recent ones transmitted by the EU Member States. While quarterly debt figures are consistent with annual debt figures at coinciding publications, differences between quarterly and annual data occur at non-coinciding publications. Differences between annual and quarterly GDP figures also occur.

For the purpose of proper consolidation of general government debt and to provide users with information, Eurostat publishes data on government loans (IGL) to other EU governments. The concepts and definitions are based on ESA 2010 and on the rules relating to the statistics for the EDP. The data covered is the stock of loans and partly deposits related to claims on other EU Member States, including loans made through the European Financial Stability Facility (EFSF). The valuation basis is the stock of loans at face value outstanding at end of each quarter. From the first quarter of 2011 onwards, the intergovernmental lending figures relate mainly to lending to Greece, Ireland and Portugal and include loans made by the EFSF.

For stock data such as general government debt, end of period exchange rates are used in the compilation of the EU aggregates. For flow data, such as GDP, average exchange rates are used. The EU aggregates, denominated in euro, can fluctuate as a result of exchange rate movements between the euro and other EU currencies.

All quarterly government finance statistics data for the first three quarters of 2025 have been labelled provisional. Country-specific metadata are published.

Geographical Information

Up to 31 December 2025, the euro area included Belgium, Germany, Estonia, Ireland, Greece, Spain, France, Croatia, Italy, Cyprus, Latvia, Lithuania, Luxembourg, Malta, the Netherlands, Austria, Portugal, Slovenia, Slovakia and Finland (EA20). From 1 January 2026, the euro area also includes Bulgaria (EA21).

The aggregate data series commented on in this Euro indicator release refer to the official composition of the euro area in the most recent quarter for which data are available. Thus, Euro indicator releases with data up to the fourth quarter of 2025 comment on EA20 series, while releases with data for the first quarter of 2026 onwards will comment on EA21 series.

The European Union includes Belgium, Bulgaria, Czechia, Denmark, Germany, Estonia, Ireland, Greece, Spain, France, Croatia, Italy, Cyprus, Latvia, Lithuania, Luxembourg, Hungary, Malta, the Netherlands, Austria, Poland, Portugal, Romania, Slovenia, Slovakia, Finland and Sweden (EU27).

For more information

Share the release

Share component will be rendered here.

* Here the title * … * … * * Here the logo and notes * */ function tableConfiguration() { /** Retrieves the list of table elements (images) */ const tables = document.getElementsByTagName(‘table’); /** We go through the list of tables */ for (let table of tables) { /** We create a section to wrap the table */ let section = document.createElement(‘section’); /** Add table-wrapper class to section element */ section.classList.add(‘table-wrapper’); /** We create a caption element to place the title */ let caption = document.createElement(‘caption’); /** We create thead element to place the headers */ let tHead = document.createElement(‘thead’); /** We create a footer to place notes, datasource and logo */ let footer = document.createElement(‘footer’); /** Retrieves the tBody */ let tBody = table.tBodies[0]; /** Retrieves the table rows */ let rows = tBody.rows; let datalabels = []; let captionContent = []; /** Stores the indexes of the rows to be removed after processing the whole table */ let rowsToRemove = []; /** We go through the rows within the table and extract caption, headers and footnotes */ buildTable(rows, captionContent, datalabels, footer, rowsToRemove); deleteRows(table, rowsToRemove); addTableCaption(caption, captionContent); addTableHeaders(table, tHead, datalabels); wrapInSection(section, table, caption, tHead, tBody, footer); linkHeaders(table); } } /** * Builds a table with the expected format: adding caption, datalabels and footer * @param {*} rows – The table rows * @param {*} captionContent – The content of the caption (title) * @param {*} datalabels – The list of datalabels (table headers) * @param {*} footer – The footer element * @param {*} rowsToRemove – Array containing the index of the rows to be removed */ function buildTable(rows, captionContent, datalabels, footer, rowsToRemove) { let rowIndex = 0; for (let row of rows) { let cells = row.cells; /** We check the first cell in order to guess the cell type: th, td; and cell classes: title, datalabel… */ let firstCell = row.firstElementChild; /** If the th class name is title, we convert this th into a caption */ if (isCaption(firstCell)) { addCaption(cells, captionContent, rowIndex, rowsToRemove) } /** If the th class name is datalabel, we add the header to the tHead component */ else if (isDatalabel(firstCell)) { addDatalabels(row, datalabels, rowIndex, rowsToRemove); } /** If the th class name is title, we convert this th into a caption */ else if (isFooter(firstCell)) { addFooter(row, footer, rowIndex, rowsToRemove); } else { row.setAttribute(‘initial-position’, rowIndex); } rowIndex++; } } /** * Checks whether the given cell has the given tagName and className * @param {*} cell – The cell to be checked * @param {*} tagName – The tag name to be checked * @param {*} className – The class name to be checked * @returns true if the cell’s tagName and className matches the given ones, false otherwise */ function checkCell(cell, tagName, className) { return cell.tagName.toLowerCase() === tagName && cell.className.indexOf(className) > -1; } /** * Whether a cell is a caption (title) * @param {*} cell – The cell to be checked * @returns true if the cell is a caption, false otherwise */ function isCaption(cell) { return checkCell(cell, ‘th’, ‘title’); } /** * Whether a cell is a datalabel * @param {*} cell – The cell to be checked * @returns true if the cell is a datalabel, false otherwise */ function isDatalabel(cell) { return checkCell(cell, ‘th’, ‘datalabel’); } /** * Whether a cell is a vertical header * @param {*} cell – The cell to be checked * @returns true if the cell is a vertical header, false otherwise */ function isVerticalHeader(cell) { return checkCell(cell, ‘th’, ‘vertical-header’); } /** * Whether a cell is a footer * @param {*} cell – The cell to be checked * @returns true if the cell is a footer, false otherwise */ function isFooter(cell) { return checkCell(cell, ‘td’, ‘footer’); } /** * Adds the content of the caption cells: paragraphs * @param {*} cells – The cells belonging to the row * @param {*} captionContent – The content of the caption * @param {*} rowIndex – The index of current row * @param {*} rowsToRemove – Array containing the index of the rows to be removed */ function addCaption(cells, captionContent, rowIndex, rowsToRemove) { for (let cell of cells) { /** We must add all the Child Nodes of each th title Cell */ for (let childNode of cell.childNodes) { captionContent.push(childNode); } } /** We remove the row from within the tables as now it’s out in the caption */ rowsToRemove.push(rowIndex); } /** * Adds the row as a datalabel * @param {*} row – The current row * @param {*} datalabels – List of datalabels * @param {*} rowIndex – The index of current row * @param {*} rowsToRemove – Array containing the index of the rows to be removed */ function addDatalabels(row, datalabels, rowIndex, rowsToRemove) { datalabels.push(row); /** We remove the row from within the tables as now it’s out in the caption */ rowsToRemove.push(rowIndex); } /** * Adds the notes and logo * @param {*} row – The current row * @param {*} footer – The footer of the table * @param {*} rowIndex – The index of current row * @param {*} rowsToRemove – Array containing the index of the rows to be removed */ function addFooter(row, footer, rowIndex, rowsToRemove) { /** We create a div element for notes and logo */ const notes = document.createElement(‘div’); notes.classList.add(‘footer-element’, ‘notes’); /** We add the content to the new note element */ notes.innerHTML = row.firstElementChild.innerHTML; /** We create a footer to place notes, datasource and logo */ footer.appendChild(notes); /** We remove the row from within the tables as now it’s out in the footer */ rowsToRemove.push(rowIndex); } /** * Deletes the rows which data has been moved to either caption, tHead or footer * @param {*} table – The current table * @param {*} rowsToRemove – Array containing the index of the rows to be removed */ function deleteRows(table, rowsToRemove) { /** We go through the title rows and append them to the caption element */ for (let j = rowsToRemove.length – 1; j >= 0; j–) { table.deleteRow(rowsToRemove[j]); } } /** * Adds the caption to the table. * @param {*} caption – The caption dom element * @param {*} captionContent – The content of the caption */ function addTableCaption(caption, captionContent) { /** We go through the Caption Content and append the nodes to the caption element */ for (let node of captionContent) { caption.appendChild(node); } } /** * Adds the headers to the table * @param {*} table – The table to make sortable * @param {*} tHead – The thead dom element * @param {*} datalabels – The datalabels to be added (headers) */ function addTableHeaders(table, tHead, dataLabels) { let sortable = false; /** We go through the header rows and append them to the tHead element */ for (let dataLabelRow of dataLabels) { let cells = dataLabelRow.cells; for (let th of cells) { if (th.classList.contains(‘sortable’)) { sortable = true; let button = createNewSortButton(); th.appendChild(button); } } tHead.appendChild(dataLabelRow); } if (sortable) { table.classList.add(‘sortable’); } } function createNewSortButton() { // creates sort button const button = document.createElement(‘button’); button.classList.add(‘ecl-table__arrow’); // creates a generic svg element for the two arrows const svgIconUp = document.createElementNS(‘http://www.w3.org/2000/svg’, ‘svg’); svgIconUp.setAttribute(‘xml:space’, ‘preserve’); svgIconUp.setAttribute(‘viewBox’, ‘0 0 24 24’); svgIconUp.setAttribute(‘enable-background’, ‘new 0 0 24 24’); svgIconUp.setAttribute(‘focusable’, ‘false’); svgIconUp.setAttribute(‘aria-hidden’, ‘true’); svgIconUp.setAttribute(‘class’, ‘ecl-table__icon ecl-icon ecl-icon–m’); // adds path element of the arrow to the arrow svg const pathIconArrow = document.createElementNS(‘http://www.w3.org/2000/svg’, ‘path’); pathIconArrow.setAttribute(‘d’, ‘M7.4 13 11 9.4c.5-.5 1.4-.5 2 0l3.6 3.6c.9.9.2 2.4-1 2.4H8.4c-1.3 0-1.9-1.5-1-2.4’); svgIconUp.appendChild(pathIconArrow); // clones the generic arrow svg including the child path also const svgIconDown = svgIconUp.cloneNode(true); // seups the arrow directions to up and down by extra css classes svgIconUp.classList.add(‘ecl-table__icon-up’); svgIconDown.classList.add(‘ecl-table__icon-down’); // insert the two arrow svg elements into the button button.appendChild(svgIconUp); button.appendChild(svgIconDown); return button; } /** * Places all the elements in their places and wraps the table into a section. * @param {*} section – The section (dom element) which will wrap table and footer * @param {*} table – The table to be wrapped * @param {*} caption – The caption (dom element) to be added to the table * @param {*} tHead – The thead (dom element) to be added to the table * @param {*} tBody – The tbody (dom element) to be added to the table * @param {*} footer – The footer (dom element) to be added to the section */ function wrapInSection(section, table, caption, tHead, tBody, footer) { /** We insert caption and thead before tbody */ table.insertBefore(caption, tBody); table.insertBefore(tHead, tBody); /** We wrap the table into a section */ table.parentNode.insertBefore(section, table); section.appendChild(table); section.appendChild(footer); } /** * Adds ids and header attributes to link every cell to all related headers so that machines can correctly read the figures. * @param {*} table – The table to be wrapped */ function linkHeaders(table) { const headers = getHeaders(table.tHead); const tBody = table.tBodies ? table.tBodies[0] : null; addHeaderLinks(tBody, headers); } function getHeaders(tHead) { const headers = []; if (tHead) { let i = 0; for (let row of tHead.rows) { /** We add a new row in the headers array */ headers.push([]); /** Stores the header ids and colSpan so we can guess to which cells (td) we must add the headers */ let j = 0; for (const cell of row.cells) { cell.setAttribute(‘id’, ‘h_’ + i + ‘_’ + j); headers[i].push({ id: cell.id, colSpan: cell.colSpan ? cell.colSpan : 1 }); j++; } i++; } } return headers; } function addHeaderLinks(tBody, headers) { if (tBody) { let i = 0; for (let row of tBody.rows) { let j = 0; for (const cell of row.cells) { /** If the cell is a vertical header we add its id */ if (cell.className.indexOf(‘vertical-header’) > -1) { cell.setAttribute(‘id’, ‘vh_’ + i + ‘_’ + j); } const headerLinks = getHeaderLinks(headers, j, cell.colSpan, row.cells[0]); cell.setAttribute(‘headers’, headerLinks); j++; } i++; } } } /** * Retrieves the header (ths) ids in order to link them to a specific cell (td) * @param {*} rows – The rows containing all headers * @param {*} index – The index of the specific cell * @param {*} colSpan – The colspan of the specific cell * @param {*} firstCell – The first cell of the row. Used to reference possible vertical headers * @returns A string containing the list of header ids split by spaces */ function getHeaderLinks(rows, index, colSpan, firstCell) { /** It will contain the header ids */ let links = ”; /** We add the reference of the vertical header if there is */ if (index > 0 && firstCell.className.indexOf(‘vertical-header’) > -1) { links = firstCell.id; } /** We go through the list of header rows */ for (const headers of rows) { /** Accumulates the colspan of the checked headers */ let sumColSpan = 0; /** We go through the list of headers for the specific header row */ for (const header of headers) { /** We check that the index of the cell belongs to a specific column */ if (index >= sumColSpan && index chartObj.resize()).observe(chartDiv); } } /** * Customizes the chart by applying custom events, axis formats, tooltips, and axis labels. * @param option – The chart options object. */ function customChartOptions(option) { setClickableDatasourceLinks(option); setAxisFormatter(option); setTooltipFormatter(option); } /** * Attaches event handlers to a chart: ‘legendselectchanged’. * * @param {Chart} chart – The chart object. */ function addEventHandlers(chart) { /** Attach an event handler for the ‘legendselectchanged’ event */ chart.on(‘legendselectchanged’, (event) => { /** Call the onChartLegendSelected function with the event and chart object */ onChartLegendSelected(event, chart); }); } /** * Handles the legend selected changed event of the chart. * @param {any} event – The event object containing the selected legend items. * @param {any} chart – The chart object. */ function onChartLegendSelected(event, chart) { /** Get the selected legend items */ const selected = event.selected; /** Count the number of selected legend items */ const selectedCount = Object.values(selected).filter(Boolean).length; /** If all legend items are deselected, keep the selection on the last clicked item */ if (selectedCount === 0) { selected[event.name] = true; } /** Update the legend selection */ chart.setOption({ legend: { selected: selected } }); } /** * Attaches custom events to the provided option object. * * @param {object} option – The option object to attach custom events to. */ function setClickableDatasourceLinks(option) { /** If the table has datasource link */ if (option.graphic && option.graphic.length >= 3) { /** Attach an onclick event handler to the third element of the graphic array (datasource links) */ option.graphic[2].onclick = function (event) { /** If the element has a link defined in its style */ if (this.style?.link) { /** Open the link in a new tab/window */ window.open(this.style.link, ‘_blank’); } }; } } /** * Sets the default axis formatter for axis labels. * @param {object} option – The option object to update the axis formatter * */ function setAxisFormatter(option) { /** The default unit used in axis labels */ const defaultUnit = ‘%’; /** If the yAxis are used as values */ if (option.yAxis.type === ‘value’) { /** If the yAxis unit is not defined, set % as default */ option.yAxis.axisLabel.unit = option.yAxis.axisLabel.unit ? option.yAxis.axisLabel.unit : defaultUnit; /** If the yAxis label padding is not defined, set 15 as default suffix */ option.yAxis.axisLabel.padding = option.yAxis?.axisLabel?.padding ? option.yAxis.axisLabel.padding : 15; /** Use the custom formatter */ option.yAxis.axisLabel.formatter = (value) => customAxisFormatter(option.lang, value, option.yAxis.axisLabel.unit, parseInt(option.yAxis.axisLabel.decimals)); // Use the custom formatter } /** If the xAxis are used as values */ if (option.xAxis.type === ‘value’) { /** If the xAxis unit is not defined, set % as default */ option.xAxis.axisLabel.unit = option.xAxis.axisLabel.unit ? option.xAxis.axisLabel.unit : defaultUnit; /** Use the custom formatter */ option.xAxis.axisLabel.formatter = (value) => customAxisFormatter(option.lang, value, option.xAxis.axisLabel.unit, parseInt(option.xAxis.axisLabel.decimals)); } /** If xAxis type is category */ else if (option.xAxis.type === ‘category’) { richStyle(option); } } /** * Formats a numeric value with a specified unit and decimal precision. * * @param {number} value – The numeric value to be formatted. * @param {string} unit – The unit to be appended to the formatted value. * @param {number} decimals – The number of decimal places to round the value to. * @returns {string} The formatted string representation of the value with unit. */ function customAxisFormatter(lang, value, unit, decimals) { /** * Check the language and format the value accordingly. * For French (fr) and German (de), use commas instead of dots. * For other languages (en), use the default format with dots. */ const formattedValue = lang === ‘fr’ || lang === ‘de’ ? `${value.toFixed(decimals).replace(‘.’, ‘,’)}${unit}` : `${value.toFixed(decimals)}${unit}`; return formattedValue; } /** * Applies custom rich text formatting to axis labels based on certain string patterns and HTML tags. * * Checks if the axis label contains certain values such as “EU”, “Euro Area”, and specific HTML tags. * It formats the labels by applying bold or italic styles as needed. * * @param axis – The axis object to which the formatting will be applied. * @returns The updated axis object with the formatted axis label. */ function richStyle(option) { /** Regular expression to match HTML tags like and */ const htmlTagRegex = /(.*?)/g; /** Mapping of HTML tags to corresponding rich text formatting styles */ const style = { strong: “bold”, em: “italic” }; /** * Formatter function for axis labels. * Converts specific label values into rich text format (e.g., bold). * * @param value – The value of the axis label to be formatted. * @returns The formatted axis label. */ option.xAxis.axisLabel.formatter = function (value) { /** Convert input string to lowercase for case-insensitive comparison */ const lowerCaseV = value ? value.toLowerCase() : ”; /** Check for “EU”, “UE”, “Euro Area”, “Euroraum” or “Zone Euro” and format them as bold */ if (lowerCaseV === ‘eu’ || lowerCaseV === ‘ue’ || lowerCaseV === ‘euro area’ || lowerCaseV === ‘euroraum’ || lowerCaseV === ‘zone euro’) { return `{bold|${value}}`; } /** Strictly check if the string contains supported HTML tags before formatting */ if (htmlTagRegex.test(value)) { /** Replace HTML tags with corresponding rich text formatting */ return value.replace(htmlTagRegex, (match, tag, content) => { return `{${style[tag]}|${content}}`; }); } /** Return the value as is if no formatting is needed */ return value; }; /** * Define the rich text styles for the axis labels. * These styles will be applied to specific HTML tags detected in the formatter function. */ option.xAxis.axisLabel.rich = { bold: { fontWeight: ‘bold’ }, italic: { fontStyle: ‘italic’ } }; } /** * Handles formatting the tooltip decimal convention in the graphic. * @remarks * This function formats the tooltip based on the language and decimal convention. * * @param {object} option – The option object to add custom tooltip. */ function setTooltipFormatter(option) { /** * Tooltip formatter for ECharts. * @param {object[]} params – Parameters for the tooltip formatter. * @param {string} params[].name – Name of the x-axis value. * @param {string} params[].seriesName – Name of the series. * @param {number} params[].value – Value of the data point. * @param {string} params[].color – Color of the series. * @param {string} ticket – Ticket for the callback. * @param {Function} callback – Callback function. * @returns {string} – Formatted tooltip content. */ option.tooltip.formatter = function (params, ticket, callback) { /** Retrieve the x-axis value for the tooltip */ const xAxisValue = params[0].name; /** If xAxisValue is empty, return null to hide the tooltip */ if (!xAxisValue) { return null; } /** Create the tooltip content container with a white background and padding */ let tooltipContent = ”; /** Add the x-axis value to the tooltip content */ /** @type {string} XAxis */ tooltipContent += ” + xAxisValue + ”; /** Retrieve the order from the tooltip option */ const sortOrder = option.tooltip.order; /** Sort the ‘params’ array based on the ‘sortOrder’ parameter. */ switch (sortOrder) { /** Sort by series name in ascending order. */ case ‘seriesAsc’: params = params.sort((a, b) => a.seriesName.localeCompare(b.seriesName)); break; /** Sort by series name in descending order. */ case ‘seriesDesc’: params = params.sort((a, b) => b.seriesName.localeCompare(a.seriesName)); break; /** Sort by numerical value in ascending order. */ case ‘valueAsc’: params = params.sort((a, b) => parseFloat(a.value) – parseFloat(b.value)); break; /** Sort by numerical value in descending order. */ case ‘valueDesc’: params = params.sort((a, b) => parseFloat(b.value) – parseFloat(a.value)); break; /** Reverse the current order. */ case ‘reverse’: params.reverse(); break; default: break; } /** Iterate over each series in the tooltip parameters */ for (const param of params) { /** Retrieve the name and value of the current series */ let seriesName = param.seriesName; let value = param.value; /** Replace every specific apostrphe by simple one. */ seriesName = seriesName.replaceAll(“’”, “‘”); /** Convert the numeric value to its string representation. */ const strValue = String(value); /** Retrieve the axis used as value */ const axis = option.yAxis.type === ‘value’ ? option.yAxis : option.xAxis; /** Initialize the number of decimal places */ let decimals = option.tooltip.decimals; /** Check if decimals configuration is set to default and the value contains decimal places */ if (!decimals && strValue.indexOf(‘.’) > -1) { /** Calculate the number of decimal places */ decimals = strValue.substring(strValue.indexOf(‘.’), strValue.length – 1).length; } else { /** If there are no decimal places, format the value with 1 decimal place */ decimals = decimals || 1; /** Convert the value to a fixed-point notation string with a specified number of decimal places. */ value = Number(value).toFixed(decimals); } /** Convert -0 value to 0 with the specified or calculated number of decimal places.*/ if (Number(value) == -0) { value = (Number(value) + 0).toFixed(decimals); } /** Extract the unit from within the axis label */ const unit = axis.axisLabel.unit ? axis.axisLabel.unit : ”; /** Format the value using the appropriate number of decimal places */ if (isNaN(value)) { value = ‘N/A’; } else { value = Number(value).toLocaleString(option.lang ? option.lang : ‘en’, { minimumFractionDigits: decimals }) + unit; } /** Retrieve the color of the current series */ const color = param.color; /** * Add the series name and value to the tooltip content * Use a circle icon with the series color */ tooltipContent += ” + ” + ” + ” + seriesName + ” + ” + ” + value + ” + ”; } /** Close the tooltip content container */ tooltipContent += ”; /** Return the formatted tooltip content */ return tooltipContent; } } /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document * * File: sortable-table.js * * Desc: Adds sorting to a HTML data table that implements ARIA Authoring Practices */ class SortableTable { constructor(tableNode) { this.tableNode = tableNode; this.columnHeaders = tableNode.querySelectorAll(‘thead th.sortable’); this.sortColumns = []; for (let i = 0; i < this.columnHeaders.length; i++) { const ch = this.columnHeaders[i]; const buttonNode = ch.querySelector('button'); if (buttonNode) { this.sortColumns.push(i); ch.setAttribute('data-column-index', i); ch.addEventListener('click', this.handleClick.bind(this)); } } this.optionCheckbox = document.querySelector( 'input[type="checkbox"][value="show-unsorted-icon"]' ); if (this.optionCheckbox) { this.optionCheckbox.addEventListener( 'change', this.handleOptionChange.bind(this) ); if (this.optionCheckbox.checked) { this.tableNode.classList.add('show-unsorted-icon'); } } } setColumnHeaderSort(columnIndex) { if (typeof columnIndex === 'string') { columnIndex = parseInt(columnIndex); } for (let i = 0; i b.value ? -1 : 1; } } else if (isNumber) { return a.value - b.value; } else { return a.value

Subscribe to receive the latest Eurostat Euro indicators releases

{ if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(mutations => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); }); } waitForElm(‘div.chart’).then((elm) => { // Has to be run after cool-ngx-editor.js script that is tweeking the charts const elements = document.getElementsByClassName(“chart”); for (let i = 0; i < elements.length; i++) { let div = document.createElement('div'); div.id = "share-chart-" + i; div.style = "position: absolute; right: 0; z-index: 50;"; elements[i].prepend(div); let script = document.createElement('script'); script.type = "application/json"; let url = new URL(window.location.href); url.searchParams.set('item', 'chart'); url.searchParams.set('id', i); let code = '{"service": "sbkm", "version": "2.0", "popup": false, "icon": true, "more" : ["facebook", "x", "linkedin", "email"], "renderTo": "' + div.id + '", "to": ["more"], "target": true, "link": "' + url.href + '", "via": "EU_Eurostat", "title": "' + getJournalArticleTitle() + '"}'; try { script.appendChild(document.createTextNode(code)); } catch (e) { // Older browsers may not support appendChild(document.createTextNode(...)) on . // Fallback to setting text directly on the script element. console.warn('Failed to append text node to script element, falling back to setting text', e); script.text = code; } elements[i].parentNode.insertBefore(script, elements[i].nextSibling); } }); // Add share button to tables $(document).ready(function() { // Has to be run after cool-ngx-editor.js script that is tweeking the tables const tables = document.getElementsByTagName("table"); for (let i = 0; i position) { await new Promise(r => setTimeout(r, 500)); // Wait a bit so that it doesn’t scroll too far allItems[position].scrollIntoView(true); } } });;}());]]>