Close Menu
Français FoisFrançais Fois
  • Actualités
  • International
  • Politique
  • Société
  • High-Tech
  • Economie
  • Divertissement
  • Culture
  • Justice
  • Police
  • Plus
    • Education
    • Communiqué de Presse
    • Mondial
Tendance
Client Challenge

Client Challenge

avril 1, 2026
Demis Hassabis, PDG de Google DeepMind, l’« optimiste prudent »

Demis Hassabis, PDG de Google DeepMind, l’« optimiste prudent »

avril 1, 2026
« Faire croire que toucher de l’argent public exclut toute forme d’autonomie de pensée alimente un ressentiment dangereux »

« Faire croire que toucher de l’argent public exclut toute forme d’autonomie de pensée alimente un ressentiment dangereux »

avril 1, 2026
« L’équilibre et la stabilité ne sont plus que des souvenirs pour les pays arabes du Golfe »

« L’équilibre et la stabilité ne sont plus que des souvenirs pour les pays arabes du Golfe »

avril 1, 2026
La compagnie ferroviaire espagnole Renfe suspend son projet de liaison à grande vitesse entre l’Espagne et Paris

La compagnie ferroviaire espagnole Renfe suspend son projet de liaison à grande vitesse entre l’Espagne et Paris

avril 1, 2026
Facebook X (Twitter) Instagram
Tendance
  • Client Challenge
  • Demis Hassabis, PDG de Google DeepMind, l’« optimiste prudent »
  • « Faire croire que toucher de l’argent public exclut toute forme d’autonomie de pensée alimente un ressentiment dangereux »
  • « L’équilibre et la stabilité ne sont plus que des souvenirs pour les pays arabes du Golfe »
  • La compagnie ferroviaire espagnole Renfe suspend son projet de liaison à grande vitesse entre l’Espagne et Paris
  • Les organes de la discorde scientifique depuis le XVIIIᵉ siècle
  • L’histoire folle d’une nouvelle pièce de Molière, coécrite avec l’IA
  • comment un traitement révolutionnaire est déployé en Eswatini, le pays le plus touché au monde
  • Confidentialité
  • Termes
  • Contacter
Histoires Web
Français FoisFrançais Fois
Bulletin S'identifier
  • Actualités
  • International
  • Politique
  • Société
  • High-Tech
  • Economie
  • Divertissement
  • Culture
  • Justice
  • Police
  • Plus
    • Education
    • Communiqué de Presse
    • Mondial
Français FoisFrançais Fois
Home » les stratégies d’alliances ont-elles fonctionné ? Vérifiez-le avec notre outil
les stratégies d’alliances ont-elles fonctionné ? Vérifiez-le avec notre outil
Politique

les stratégies d’alliances ont-elles fonctionné ? Vérifiez-le avec notre outil

PersonnelBy Personnelmars 24, 2026

Les résultats du second tour des élections municipales, qui s’est tenu dimanche 22 mars, sont désormais complets. Ils permettent de mesurer l’écart entre les projections théoriques et la réalité des bulletins glissés dans les urnes. Et de constater que les fusions, même lorsqu’elles plaçaient une liste en position dominante, n’ont pas toujours suffi à la faire élire.

A l’issue du premier tour, il était en effet possible de calculer le « potentiel électoral » de chaque liste : une addition des scores des listes qualifiées et, le cas échéant, de celles qu’elles avaient absorbées. Mais les reports de voix n’ont pas toujours eu lieu dans les proportions anticipées. Dans 233 communes de plus de 3 500 habitants, la liste qui semblait la mieux armée pour l’emporter a finalement été battue – 110 de ces listes étaient issues d’une fusion. Dans les villes de plus de 30 000 habitants, 38 listes fusionnées et mieux placées pour l’emporter ont échoué, contre 33 qui ont réussi leur pari.

Voir aussi | Article réservé à nos abonnés La carte définitive des résultats des municipales au second tour

Par exemple, à Brest, la liste fusionnée du Parti socialiste (PS) et de La France insoumise (LFI) a obtenu des suffrages proches des résultats obtenus par les deux listes au premier tour. Le Rassemblement national (RN) a, lui, perdu près de 3 000 voix, tandis que le candidat Les Républicains (LR), Stéphane Roudaut, a fédéré 14 272 électeurs de plus qu’au premier tour – 27 points de plus que son « potentiel électoral » –, ce qui lui a assuré la victoire.

A Nantes, en revanche, la fusion PS-LFI a permis à la maire sortante, Johanna Rolland, de conserver sa place face à la liste LR de Foulques Chombart de Lauwe. Johanna Rolland a récolté environ 8 500 voix de plus qu’au premier tour – les quelque 18 400 voix supplémentaires données à son rival n’ont pas suffi à inverser la tendance.

Le module interactif ci-dessous vous permet de comparer, pour chaque ville ayant organisé un second tour, le potentiel électoral théorique et les résultats officiels du second tour. Vous pouvez filtrer les communes par taille, type d’alliance ou bloc politique.

Comparez les résultats officiels et le potentiel électoral des listes candidates pour le second tour

Afficher les communes de…

+ de 30 000 habitants

– de 30 000 habitants

Filtrer

Liste dominante en échec

Toutes les fusions

Les fusions gagnantes

Les fusions infructueuses

LFI au second tour

Gauche au second tour

Droite au second tour

Ext. droite au second tour

Trier

Population

Plus grand écart potentiel/résultat

${subtitle ? `

${subtitle}

` : « }
`;

grid.appendChild(card);

const chartEl = card.querySelector(« .mun_alliances_card_chart »);
const width = chartEl.offsetWidth || card.offsetWidth;
const isSmall = width < 600;

const margin = {
top: isSmall ? width * 0.02 : width * 0.02,
right: width * 0.01,
bottom: isSmall ? width * 0.01 : width * 0.01,
left: isSmall ? width * 0.001 : width * 0.001
};

const svg = d3.select(chartEl)
.append(« svg »)
.attr(« width », « 100% »)
.attr(« class », « chart »);

let x0 = 0;
const t1Segments = [];
const t1Rows = commune.listes;
for (const row of t1Rows) {
const pct = row.pct_exprimes;
if (!Number.isFinite(pct) || pct <= 0) continue;
const x1 = x0 + pct;
const nuanceMin = row.nuance;
const nuanceLM = row.nuance_lemonde;
const { light, dark, label } = getNuanceStyle(nuanceLM, nuanceMin);
const nuanceCode = String(nuanceLM || nuanceMin || label);
t1Segments.push({
ordre: row.ordre,
nuanceCode,
nuanceLabel: String(label),
pct,
x0,
x1,
light,
dark,
sourceRow: row,
t1Status: row.qualifie,
lib: row.lib ?? «  »,
deptLabel
});
x0 = x1;
}

const missing = Math.max(0, 100 – x0);

if (missing > 0.05) {
t1Segments.push({
nuanceLabel: « Eliminés »,
nuanceCode: « __remainder__ »,
voix: null,
pct: missing,
x0: x0,
x1: 100,
light: « #E2E4E9 »,
dark: « #3C3C3C »,
isRemainder: true,
lib: commune.lib,
deptLabel,
});
}

let x0t2 = 0;
const t2Segments = [];
const fmtDecision = (s) => String(s || «  »).trim();
for (const row of t1Rows) {
const decision = fmtDecision(row.decision_officielle);
if (decision == « Fusion (absorbée) ») continue;
if (decision.startsWith(« Désistement »)) continue;
if (decision.startsWith(« Fusionnable »)) continue;
if (decision === « Ne fusionne pas ») continue;
if (decision === « Maintien ») {
const pct = row.pct_exprimes;
if (!Number.isFinite(pct) || pct <= 0) continue;
const x1 = x0t2 + pct;
const { light, dark, label } = getNuanceStyle(row.nuance_lemonde, row.nuance);
t2Segments.push({
nuance: String(label),
pct,
x0: x0t2,
x1,
light,
dark,
isRemainder: false,
sourceRows: [row],
decision: « Maintien »,
lib: commune.lib,
deptLabel
});
x0t2 = x1;
continue;
}
if (decision === « Fusion (absorbante) ») {
let pct = row.pct_exprimes;
const sourceRows = [row];

const fusionTargets = String(row.n_panneau_absorbe || «  »)
.split(« ; »)
.map((s) => {
const n = parseFR(s);
return Number.isFinite(n) ? String(Math.trunc(n)) : String(s || «  »).trim();
})
.filter(Boolean);
const seen = new Set();
for (const targetPanneau of fusionTargets) {
if (seen.has(targetPanneau)) continue;
seen.add(targetPanneau);
const absorbed = commune.byPanneau.get(targetPanneau);
if (absorbed && Number.isFinite(absorbed.pct_exprimes)) {
pct += absorbed.pct_exprimes;
sourceRows.push(absorbed);
}
}
// if (fusionTargets.length && sourceRows.length === 1) {
// console.log(« Fusion cible introuvable (panneau) », commune.code_circo, row.n_panneau, fusionTargets);
// }
if (!Number.isFinite(pct) || pct <= 0) continue;
const x1 = x0t2 + pct;
const { light, dark } = getNuanceStyle(row.nuance_lemonde, row.nuance);
const fusionLabel = sourceRows
.map((r) => getNuanceStyle(r.nuance_lemonde, r.nuance).label)
.join(« + »);
t2Segments.push({
nuance: fusionLabel,
pct,
x0: x0t2,
x1,
light,
dark,
isRemainder: false,
sourceRows,
decision: « Fusion (absorbante) »,
lib: commune.lib,
deptLabel
});
x0t2 = x1;
continue;
}

}
const aRepartir = Math.max(0, 100 – x0t2);

if (aRepartir > 0.05) {
t2Segments.push({
nuance: « À répartir »,
pct: aRepartir,
x0: x0t2,
x1: Math.min(100, x0t2 + aRepartir),
light: « #E2E4E9 »,
dark: « #3C3C3C »,
isRemainder: true,
decision: « À répartir »,
lib: commune.lib,
deptLabel
});
x0t2 = x0t2 + aRepartir;
}

const remainder = t2Segments.filter(d => d.isRemainder);
const nonRemainder = t2Segments.filter(d => !d.isRemainder);
nonRemainder.sort((a, b) => b.pct – a.pct);

let cursor = 0;
for (const seg of […nonRemainder, …remainder]) {
seg.x0 = cursor;
seg.x1 = cursor + seg.pct;
cursor = seg.x1;
}
t2Segments.length = 0;
t2Segments.push(…nonRemainder, …remainder);

// Tour 2 officiel
let x0t3 = 0;
const t3Segments = [];
for (const row of t1Rows) {
const pct = row.resultat_t2;
if (!Number.isFinite(pct) || pct <= 0) continue;
const x1 = x0t3 + pct;
const { light, dark, label } = getNuanceStyle(row.nuance_lemonde, row.nuance);
t3Segments.push({
pct, x0: x0t3, x1, light, dark,
sourceRow: row,
lib: commune.lib, deptLabel,
isT3: true,
});
x0t3 = x1;
}

const t3Remainder = t3Segments.filter(d => d.isRemainder);
const t3NonRemainder = t3Segments.filter(d => !d.isRemainder);
t3NonRemainder.sort((a, b) => {
const aElu = a.sourceRow?.elu === « O »;
const bElu = b.sourceRow?.elu === « O »;
if (aElu && !bElu) return -1;
if (!aElu && bElu) return 1;
return b.pct – a.pct;
});

// Recalculer les x0/x1 après tri
let cursorT3 = 0;
for (const seg of […t3NonRemainder, …t3Remainder]) {
seg.x0 = cursorT3;
seg.x1 = cursorT3 + seg.pct;
cursorT3 = seg.x1;
}
t3Segments.length = 0;
t3Segments.push(…t3NonRemainder, …t3Remainder);
const hasT3Data = t3Segments.length > 0;
const missingT3 = Math.max(0, 100 – x0t3);
if (hasT3Data && missingT3 > 0.05) {
t3Segments.push({
pct: missingT3, x0: x0t3, x1: 100,
light: « #E2E4E9 », dark: « #3C3C3C »,
isRemainder: true,
lib: commune.lib, deptLabel,
isT3: true,
});
}

const labelW = isSmall ? 78 : 78;
const innerW = width – margin.left – margin.right – labelW;
const barH = 20;
const barY = 4;
const gapY = isSmall ? 18 : 22;
const barY2 = barY + barH + gapY;
const gapY3 = isSmall ? 18 : 22;

const barY3 = barY2 + barH + gapY3;
const neededH = hasT3Data
? margin.top + barY3 + barH + margin.bottom
: margin.top + barY2 + barH + margin.bottom;

svg.attr(« viewBox », [0, 0, width, neededH]);

const x = d3.scaleLinear().domain([0, 100]).range([0, innerW]);
const g = svg
.append(« g »)
.attr(« transform », `translate(${margin.left + labelW},${margin.top})`);

const t1SegByRow = new Map(
t1Segments
.filter(d => d.sourceRow)
.map(d => [d.sourceRow, d])
);

// Gérer les liens

const fusionTargets = t2Segments.filter(d => d.decision === « Fusion (absorbante) » && Array.isArray(d.sourceRows) && d.sourceRows.length);
if (fusionTargets.length) {
const gLinks = g.append(« g »).attr(« class », « t2-sankey-links »).attr(« pointer-events », « none »);

for (const ft of fusionTargets) {
const components = (ft.sourceRows || [])
.map(sr => {
const t1Seg = t1SegByRow.get(sr);
const pct = Number.isFinite(sr?.pct_exprimes) ? sr.pct_exprimes : (t1Seg?.pct ?? 0);
return { sr, t1Seg, pct };
})
.filter(d => d.t1Seg && Number.isFinite(d.pct) && d.pct > 0)
.sort((a, b) => b.pct – a.pct);

const denomUsed = components.reduce((s, c) => s + c.pct, 0) || ft.pct || 1;

const ySourceBottom = barY + barH;
const yTargetTop = barY2;

let accX = 0;
const xTargetBase = x(ft.x0);
const xTargetTotalW = Math.max(0, x(ft.x1) – x(ft.x0));

if (!components.length) continue;
for (const comp of components) {
const share = comp.pct / denomUsed;
const sLeft = x(comp.t1Seg.x0);
const sRight = x(comp.t1Seg.x1);
const tLeft = xTargetBase + accX * xTargetTotalW;
const tRight = tLeft + share * xTargetTotalW;
accX += share;

const strokeColor = isDark ? comp.t1Seg.dark : comp.t1Seg.light;

const pathD =
`M ${sLeft} ${ySourceBottom}
L ${sRight} ${ySourceBottom}
L ${tRight} ${yTargetTop}
L ${tLeft} ${yTargetTop}
Z`;
gLinks.append(« path »)
.attr(« d », pathD)
.attr(« fill », strokeColor)
.attr(« fill-opacity », 0.22)
.attr(« stroke », strokeColor)
.attr(« stroke-opacity », 0.35)
.attr(« stroke-width », 1);
}
}
}
g.append(« text »)
.attr(« class », « bar-label passelect »)
.attr(« x », -8)
.attr(« y », barY + barH / 2)
.attr(« dy », « 0.35em »)
.attr(« text-anchor », « end »)
.text(« Résultats T1 »);

g.selectAll(« rect.t1 »)
.data(t1Segments)
.enter()
.append(« rect »)
.attr(« class », « t1 »)
.attr(« x », (d) => x(d.x0))
.attr(« y », barY)
.attr(« height », barH)
.attr(« width », (d) => Math.max(0, x(d.x1) – x(d.x0)))
.attr(« fill », (d) => (isDark ? d.dark : d.light))
.attr(« stroke », isDark ? « rgba(255,255,255,0.35) » : « rgba(0,0,0,0.18) »)
.attr(« stroke-width », 1)
.attr(« fill-opacity », (d) => (d.t1Status === « Fusionnable » ? isDark ? 0.6 : 0.35 : 1))
.attr(« stroke-dasharray », (d) => (d.t1Status === « Fusionnable » ? « 3 2 » : null))
.attr(« shape-rendering », « crispEdges »)
.attr(« cursor », « pointer »)
.on(« mouseenter », showTooltip)
.on(« mousemove », showTooltip)
.on(« mouseleave », hideTooltip);

t1Segments
.filter(d => d.sourceRow?.decision_officielle?.startsWith(« Désistement »))
.forEach(d => {
const segW = Math.max(0, x(d.x1) – x(d.x0));
const cx = x(d.x0) + segW / 2;
const cy = barY + (barH / 2);
const pictoG = g.append(« g »).attr(« class », « t1-desistement-picto »);

pictoG.append(« text »)
.attr(« x », cx)
.attr(« y », cy)
.text(« × »);
});

g.append(« text »)
.attr(« class », « bar-label passelect »)
.attr(« x », -8)
.attr(« y », barY2 + barH / 2)
.attr(« dy », « 0.35em »)
.attr(« text-anchor », « end »)
.text(« Potentiel électoral »);

g.selectAll(« rect.t2 »)
.data(t2Segments)
.enter()
.append(« rect »)
.attr(« class », « t2 »)
.attr(« x », (d) => x(d.x0))
.attr(« y », barY2)
.attr(« height », barH)
.attr(« width », (d) => Math.max(0, x(d.x1) – x(d.x0)))
.attr(« fill », (d) => (isDark ? d.dark : d.light))
.attr(« fill-opacity », 1)
.attr(« stroke », isDark ? « rgba(255,255,255,0.8) » : « rgba(0,0,0,0.3) »)
.attr(« stroke-width », 1)
.attr(« stroke-linecap », « round »)
.attr(« stroke-linejoin », « round »)
.attr(« stroke-dasharray », « 4 3 »)
.attr(« cursor », « pointer »)
.on(« mouseenter », showTooltip)
.on(« mousemove », showTooltip)
.on(« mouseleave », hideTooltip);

if (hasT3Data) {
g.append(« text »)
.attr(« class », « bar-label passelect »)
.attr(« x », -8)
.attr(« y », barY3 + barH / 2)
.attr(« dy », « 0.35em »)
.attr(« text-anchor », « end »)
.text(« Résultats T2 »);

g.selectAll(« rect.t3 »)
.data(t3Segments)
.enter()
.append(« rect »)
.attr(« class », « t3 »)
.attr(« x », d => x(d.x0))
.attr(« y », barY3)
.attr(« height », barH)
.attr(« width », d => Math.max(0, x(d.x1) – x(d.x0)))
.attr(« fill », d => isDark ? d.dark : d.light)
.attr(« stroke », isDark ? « rgba(255,255,255,0.35) » : « rgba(0,0,0,0.18) »)
.attr(« stroke-width », 1)
.attr(« shape-rendering », « crispEdges »)
.attr(« cursor », « pointer »)
.on(« mouseenter », showTooltip)
.on(« mousemove », showTooltip)
.on(« mouseleave », hideTooltip);

t3Segments
.filter(d => !d.isRemainder && Number.isFinite(d.sourceRow?.diff_potentiel_t2))
.forEach((d, i) => {
const diff = d.sourceRow.diff_potentiel_t2;
const absD = Math.abs(diff);
if (absD < 0.05) return;
const segW = Math.max(0, x(d.x1) – x(d.x0));
if (segW < 14) return; // trop étroit
const cx = x(d.x0) + segW / 2;
const sign = diff > 0 ? « + » : « u2212 »;
const unit = i === 0 ? (absD >= 2 ? « u00A0pts » : « u00A0pt ») : «  »;
const label = `${sign}${absD.toFixed(1).replace(« . », « , »)}${unit}`;
// positif = candidat a fait mieux que le potentiel
const color = diff > 0 ? « #2a9d2a » : « #d9522a »;
g.append(« text »)
.attr(« class », « t3-diff-label passelect »)
.attr(« x », cx)
.attr(« y », barY3 – 2)
.attr(« dy », « -0.15em »)
.attr(« text-anchor », « middle »)
.attr(« fill », color)
.text(label);
});
}
}

function showTooltip(event, d) {
const isT3 = !!d.isT3;
const isT2 = !isT3 && !!d.decision;
const isT1 = !isT2 && !isT3;

const row = d.sourceRow || (Array.isArray(d.sourceRows) ? d.sourceRows[0] : null);
let htmlTooltip = «  »;
const titleCity = d.lib ? `

${d.lib}

` : «  »;

// On a des résultats
if (row) {
const { light, dark, label: nuanceLabel } = getNuanceStyle(row.nuance_lemonde, row.nuance);
const nuanceColor = isDark ? dark : light;
const nuanceColorDark = dark || light;
const civ = row.tete_civ ? `${row.tete_civ} ` : «  »;
const fullName = `${civ}${row.tete_prenom ?? «  »} ${row.tete_nom ?? «  »}`.trim();
let secondLine = «  »;
if (isT1) {
secondLine = `${fmtPct(d.pct)} des suffrages exprimés au premier tour`;
} else if (isT2) {
secondLine = `Potentiel électoral : ${fmtPct(d.pct)}`;
} else if (isT3) {
secondLine = `${fmtPct(d.pct)} des suffrages exprimés au second tour`;
}
// Lignes additionnelles (fusion)
let extraContent = «  »;
const badge = (isT3 && d.sourceRow?.elu === « O »)
? `



`
: «  »;
if (isT3 && Number.isFinite(row.diff_potentiel_t2)) {
const diff = row.diff_potentiel_t2;
const absD = Math.abs(diff);
if (absD >= 0.05) {
const sign = diff > 0 ? « + » : « u2212 »;
const unit = absD >= 2 ? « pts » : « pt »;
const diffColor = diff > 0 ? « #2a9d2a » : « #d9522a »;
const voix = row.diff_potentiel_t2_voix;
const voixStr = Number.isFinite(voix)
? ` (${voix > 0 ? « + » : voix < 0 ? « u2212 » : «  »}${thousands(Math.abs(Math.round(voix)))} voix)`
: «  »;
extraContent = `${sign}${absD.toFixed(1).replace(« . », « , »)}u00A0${unit} ${voixStr} par rapport au potentiel électoral`;
}
}
if (isT1 && d.t1Status === « Fusionnable ») {
extraContent = `Liste éliminée mais fusionnable`;
}
if (isT1 && d.sourceRow?.decision_officielle?.startsWith(« Désistement »)) {
extraContent = `×Désistement au second tour`;
}
if (isT2 && d.decision === « Fusion (absorbante) ») {
const fused = (Array.isArray(d.sourceRows) ? d.sourceRows.slice(1) : []).filter(Boolean);
if (fused.length) {
extraContent = fused.map(r => {
const { light, dark } = getNuanceStyle(r.nuance_lemonde, r.nuance);
const nu2 = getNuanceStyle(r.nuance_lemonde, r.nuance).label;
const civ2 = r.tete_civ ? `${r.tete_civ} ` : «  »;
const name2 = `${civ2}${r.tete_prenom ?? «  »} ${r.tete_nom ?? «  »}`.trim();
const dotColor = isDark ? dark : light;
const dot = ``;
return `${dot}Fusion avec ${name2} (${nu2})`;
}).join(« 
« );
}
}
const extra = extraContent
? `

${extraContent}

`
: «  »;
htmlTooltip = `
${titleCity}

  1. ${fullName} (${nuanceLabel}) ${badge} ${d.sourceRow?.elu === « O » ? d.sourceRow?.tete_civ === « Mme » ? « élue »: « élu » : «  »}

    ${secondLine}

${extra}

`;
} else {
if (isT1) {
htmlTooltip = `${titleCity}

Autres listes éliminéess : ${fmtPct(d.pct)}

`;
}
else if (isT2 && d.isRemainder) {
htmlTooltip = `${titleCity}

À répartir : ${fmtPct(d.pct)}

`;
} else if (isT3 && d.isRemainder) {
// CHELOU
htmlTooltip = `${titleCity}

Autre : ${fmtPct(d.pct)}

`;
} else {
return;
}
}

tooltip
.html(htmlTooltip)
.style(« opacity », 1);
if (window.innerWidth > 600) {
tooltip
.style(« left », (event.pageX – tooltip.node().getBoundingClientRect().width / 2) + « px »)
.style(« top », (event.pageY – tooltip.node().getBoundingClientRect().height – 8) + « px »);
}
}

function hideTooltip() {
tooltip.style(« opacity », 0);
}

const communes = Object.values(resultsByCommune).sort(
(a, b) => (b.population ?? 0) – (a.population ?? 0)
);

communes.forEach((commune, idx) => renderCommune(commune, idx));
loader.classList.add(« hidden »);

const loadMoreBtn = document.getElementById(« mun_alliances_load_more »);

function applyFilter() {
const cards = document.querySelectorAll(« .mun_alliances_card »);

let visibleTotal = 0;
let shownSoFar = 0;
cards.forEach(card => {
let matches = true;
if (activePopFilter === « pop_lt30 »)
matches = matches && card.dataset.popcat === « lt30 »;
else if (activePopFilter === « pop_gt30 »)
matches = matches && card.dataset.popcat === « gt30 »;
if (activeThematic.has(« fusion »))
matches = matches && card.dataset.fusion === « 1 »;
if (activeThematic.has(« fusion_gagnante »))
matches = matches && card.dataset.fusion_gagnante === « 1 »;
if (activeThematic.has(« fusion_infructueuse »))
matches = matches && card.dataset.fusion_infructueuse === « 1 »;
if (activeThematic.has(« gauche »))
matches = matches && card.dataset.gauche === « 1 »;
if (activeThematic.has(« droite »))
matches = matches && card.dataset.droite === « 1 »;
if (activeThematic.has(« extdroite »))
matches = matches && card.dataset.extdroite === « 1 »;
if (activeThematic.has(« lfi »))
matches = matches && card.dataset.lfi === « 1 »;
if (activeThematic.has(« inversion »))
matches = matches && card.dataset.inversion === « 1 »;
if (matches) {
visibleTotal++;
const withinPage = shownSoFar < shownCount;
card.style.display = withinPage ? «  » : « none »;
if (withinPage) shownSoFar++;
} else {
card.style.display = « none »;
}
});
loadMoreBtn.style.display = visibleTotal > shownCount ? «  » : « none »;
}

function sortCommunes() {
const cards = […document.querySelectorAll(« .mun_alliances_card »)];
cards.sort((a, b) => {
const cA = resultsByCommune[a.dataset.code];
const cB = resultsByCommune[b.dataset.code];
if (currentSort === « diff ») {
return (cB?.maxDiffAbs ?? 0) – (cA?.maxDiffAbs ?? 0);
}
return (cB?.population ?? 0) – (cA?.population ?? 0);
});
const grid = document.querySelector(« .mun_alliances_charts_grid »);
cards.forEach(card => grid.appendChild(card));
shownCount = PAGE_SIZE;
applyFilter();
}

const listeComm = communes.map((c) => {
const codeCirco5 = String(c.code_circo ?? «  »).padStart(5, « 0 »);
const dept = codeCirco5.slice(0, 2);
return {
label: { name: `${c.lib} (${dept})` },
data: { code: c.code_circo },
};
});
const func_to_treat_result_donnees = (result) => {
const code = result?.data?.code;
activePopFilter = « pop_gt30 »;
activeThematic.clear();
// Filtres sur +30000
const pillsEls = document.querySelectorAll(« .mun_alliances_filters_pills .legend_bloc »);
pillsEls.forEach(p => {
p.classList.toggle(« legend_bloc__active », p.dataset.filter === « pop_gt30 »);
});
// On remet tout en affiché
applyFilter();
const cards = document.querySelectorAll(« .mun_alliances_card »);
cards.forEach(card => {
card.style.display = card.dataset.code === code ? «  » : « none »;
});
searchActive = true;
loadMoreBtn.innerHTML = `Réinitialiser`;
loadMoreBtn.style.display = «  »;
};
const reset_func = () => {
// const input = document.querySelector(`.lmui-search__bar input`);
// if (input) input.value = «  »;
document.querySelector(« #search_mun_alliances .lmui-search__reset-button »)?.click();

searchActive = false;
activePopFilter = « pop_gt30 »;
activeThematic.clear();
shownCount = PAGE_SIZE;
pills.forEach(p => p.classList.toggle(« legend_bloc__active », p.dataset.filter === « pop_gt30 »));
loadMoreBtn.innerHTML = `Afficher plus de communes

`;
applyFilter();
};
autocomplete_decodeurs(
« search_mun_alliances »,
listeComm,
func_to_treat_result_donnees,
reset_func,
(x) => `${x.label.name}`,
6,
3,
slugify,
« Aucune commune avec ce nom »
);
document.addEventListener(« keydown », function (event) {
if (event.key === « Escape ») reset_func();
});

const pills = document.querySelectorAll(« .mun_alliances_filters_pills .legend_bloc »);
const popFilters = new Set([« pop_lt30 », « pop_gt30 »]);
const thematicFilters = new Set([« fusion », « gauche », « droite », « extdroite », « lfi », « inversion », « fusion_infructueuse », « fusion_gagnante »]);

pills.forEach(pill => {
pill.addEventListener(« click », () => {
const f = pill.dataset.filter;
if (searchActive) {
searchActive = false;
document.querySelector(« #search_mun_alliances .lmui-search__reset-button »)?.click();
loadMoreBtn.innerHTML = `Afficher plus de communes

`;
}
if (popFilters.has(f)) {
// Mutuellement exclusifs : toggle
activePopFilter = activePopFilter === f ? null : f;
shownCount = PAGE_SIZE;
} else if (thematicFilters.has(f)) {
// Toggle indépendant
if (activeThematic.has(f)) activeThematic.delete(f);
else activeThematic.add(f);
shownCount = PAGE_SIZE;
}
pills.forEach(p => {
const pf = p.dataset.filter;
const isActive =
pf === « all » ? (activePopFilter === null && activeThematic.size === 0) :
popFilters.has(pf) ? activePopFilter === pf :
thematicFilters.has(pf) ? activeThematic.has(pf) : false;
p.classList.toggle(« legend_bloc__active », isActive);
});
applyFilter();
});
});

const sortPills = document.querySelectorAll(« #mun_alliances_sort_pills .legend_bloc »);
sortPills.forEach(pill => {
pill.addEventListener(« click », () => {
sortPills.forEach(p => p.classList.remove(« legend_bloc__active »));
pill.classList.add(« legend_bloc__active »);
currentSort = pill.dataset.sort;
sortCommunes();
});
});

applyFilter();

// Watch back to top
const backBtn = document.getElementById(« mun_alliances_backtotop »);
const topEl = document.querySelector(« .mun_alliances_top »);
window.addEventListener(« scroll », () => {
const threshold = topEl
? topEl.getBoundingClientRect().bottom + window.scrollY + 300
: 300;
backBtn.classList.toggle(« visible », window.scrollY > threshold);
});
backBtn.addEventListener(« click », () => {
document.getElementById(« search_mun_alliances »).scrollIntoView({ behavior: « smooth », block: « center » });
document.querySelector(« #search_mun_alliances input »)?.focus();
});

loadMoreBtn.addEventListener(« click », () => {
if (searchActive) {
reset_func();
} else {
shownCount += PAGE_SIZE;
applyFilter();
}
});

}

// Tout lancer une première fois

drawAllAlliances();

Share. Facebook Twitter LinkedIn Telegram WhatsApp Email

Continue de lire

« Faire croire que toucher de l’argent public exclut toute forme d’autonomie de pensée alimente un ressentiment dangereux »

« Faire croire que toucher de l’argent public exclut toute forme d’autonomie de pensée alimente un ressentiment dangereux »

Simplification de la vie économique : le gouvernement au secours des ZFE pour mieux sauver son projet de loi

Simplification de la vie économique : le gouvernement au secours des ZFE pour mieux sauver son projet de loi

Retraites : les résultats de l’Agirc-Arrco toujours en excédent, malgré un net recul

Retraites : les résultats de l’Agirc-Arrco toujours en excédent, malgré un net recul

Emmanuel Macron répond à Donald Trump, qui accuse la France de ne pas coopérer avec les Etats-unis dans le conflit qui les oppose à l’Iran

Emmanuel Macron répond à Donald Trump, qui accuse la France de ne pas coopérer avec les Etats-unis dans le conflit qui les oppose à l’Iran

Présidentielle : chez LR, une tribune répond à une autre au sujet du candidat unique de la droite et du centre

Présidentielle : chez LR, une tribune répond à une autre au sujet du candidat unique de la droite et du centre

La « nouvelle France », une expression banale transformée en grenade dégoupillée

La « nouvelle France », une expression banale transformée en grenade dégoupillée

Marc Lazar, historien : « La grande force du national-populisme est de répondre à une colère sociale par un récit mobilisateur »

Marc Lazar, historien : « La grande force du national-populisme est de répondre à une colère sociale par un récit mobilisateur »

Le gouvernement entend accélérer l’électrification et une sortie progressive des hydrocarbures

Le gouvernement entend accélérer l’électrification et une sortie progressive des hydrocarbures

Municipales à Toulouse : la Ville rose reste bel et bien un bastion de droite

Municipales à Toulouse : la Ville rose reste bel et bien un bastion de droite

Add A Comment
Leave A Reply Cancel Reply

Choix de l'éditeur

Demis Hassabis, PDG de Google DeepMind, l’« optimiste prudent »

Demis Hassabis, PDG de Google DeepMind, l’« optimiste prudent »

avril 1, 2026
« Faire croire que toucher de l’argent public exclut toute forme d’autonomie de pensée alimente un ressentiment dangereux »

« Faire croire que toucher de l’argent public exclut toute forme d’autonomie de pensée alimente un ressentiment dangereux »

avril 1, 2026
« L’équilibre et la stabilité ne sont plus que des souvenirs pour les pays arabes du Golfe »

« L’équilibre et la stabilité ne sont plus que des souvenirs pour les pays arabes du Golfe »

avril 1, 2026
La compagnie ferroviaire espagnole Renfe suspend son projet de liaison à grande vitesse entre l’Espagne et Paris

La compagnie ferroviaire espagnole Renfe suspend son projet de liaison à grande vitesse entre l’Espagne et Paris

avril 1, 2026

Derniers Articles

Les organes de la discorde scientifique depuis le XVIIIᵉ siècle

Les organes de la discorde scientifique depuis le XVIIIᵉ siècle

avril 1, 2026
L’histoire folle d’une nouvelle pièce de Molière, coécrite avec l’IA

L’histoire folle d’une nouvelle pièce de Molière, coécrite avec l’IA

avril 1, 2026
comment un traitement révolutionnaire est déployé en Eswatini, le pays le plus touché au monde

comment un traitement révolutionnaire est déployé en Eswatini, le pays le plus touché au monde

avril 1, 2026
Facebook X (Twitter) Pinterest TikTok Instagram
© 2026 Français Fois. Tous droits réservés.
  • Politique de Confidentialité
  • Termes et Conditions
  • Contacter

Type above and press Enter to search. Press Esc to cancel.