PageSourceSearch

https://www.uu.se/download/18.579c19d717c11707086bbab/1777267188644/same-heights-for-puffs.js

js uu.se collected 2026-09-27 20:54:07 UTC 5,100 bytes, 138 lines download raw bytes

1/*
2 * This Javascript will resize all puffs on the page to the height of their biggest sibling.
3 *
4 * Source: misc-js/same-heights-for-puffs.js
5 */
6
7(() => {
8  const STYLE_SHEET_ID = 'custom-puff-heights';
9  const MAIN_COLUMN_SELECTOR = '#Mittenspalt';
10  const PUFF_SELECTOR = '.sv-se-uu-uit-webb-puff, .sv-se-uu-uit-webb-webapp-linked-cards-event, .sv-portlet:has([id^="Lankadmodul"]), .sv-se-uu-uit-webb-webapp-linked-cards-archive, .sv-se-uu-uit-webb-webapp-education-programme-link-pages';
11  const CARD_BODY_SELECTOR = '.uu-blurb__content';
12  const CARD_SELECTOR = `${CARD_BODY_SELECTOR}`;
13  const PARENT_SELECTOR = '.sv-layout .sv-row, .sv-layout .sv-horizontal, .sv-layout .sv-vertical';
14  const CUSTOM_CLASS_PREFIX = 'custom-height-';
15  const BREAKPOINT = 768;
16  const MAIN_COLUMN_ELEMENT = document.querySelector(MAIN_COLUMN_SELECTOR);
17
18  /**
19   *  Gets a style sheet element in which to put custom CSS rules.
20   *  Creates it if necessary.
21   */
22  const getCustomStyle = () => {
23    let style = document.getElementById(STYLE_SHEET_ID);
24    if (!style) {
25      style = document.createElement('style');
26      style.id = STYLE_SHEET_ID;
27      document.head.appendChild(style);
28    }
29    return style;
30  };
31
32  /**
33   * Removes the custom style sheet from the DOM and its rules with it.
34   */
35  const removeCustomStyle = () => {
36    const style = document.getElementById(STYLE_SHEET_ID);
37    if (style) {
38      style.disabled = true;
39      style.parentNode.removeChild(style);
40    }
41  };
42
43  /**
44   * Tries to find an ancestor element that probably contains this puff's
45   * siblings by matching against a set of CSS classes.
46   */
47  const findPuffContainer = (puff) => {
48    let element = puff;
49    while (
50      !element.matches(PARENT_SELECTOR) &&
51      element.parentNode &&
52      // Don't go as high as the "Mittenspalt", since that would put all puffs
53      // in the same container.
54      element.parentNode !== MAIN_COLUMN_ELEMENT
55    ) {
56      element = element.parentNode;
57    }
58    return element;
59  };
60
61  /**
62   * Finds all puffs on the page and makes them the same height as their immediate siblings.
63   * The biggest puff decides the height.
64   */
65  const setPuffsToSameHeight = (puffSelector, cardSelector) => {
66    // Find the all the Sitevision generated divs that hold a number of puffs each.
67    // Since they have no good classes we need to find a puff generated by our puff
68    // module and then use its grand parent.
69    const puffSelectorList = [...document.querySelectorAll(puffSelector)];
70    const puffContainers = puffSelectorList
71      .map((puff) => {
72        return findPuffContainer(puff);
73      })
74      // Remove duplicates
75      .filter((value, index, self) => {
76        return self.indexOf(value) === index;
77      });
78
79    // For each of these "puff containers" we'll iterate over its puffs and find the biggest height.
80    // Then we'll resize all of the puffs in the puff container to that height.
81    puffContainers.forEach((puffContainer) => {
82      // We want to exclude the main container that includes everything
83      if (!puffContainer.outerHTML.includes('<main')) {
84        let maxHeight = 0;
85        // Find biggest height
86        const cards = [...puffContainer.querySelectorAll(cardSelector)];
87        cards.forEach(
88          (card) => {
89            maxHeight = Math.max(maxHeight, card.offsetHeight);
90          }
91        );
92
93        // Create a new CSS class
94        const customClass = CUSTOM_CLASS_PREFIX + Math.floor(Math.random() * 100000000);
95
96        // First, remove any previous custom height classes
97        const puffContainerClassList = puffContainer.classList;
98        puffContainerClassList.forEach((className) => {
99          if (className.startsWith(CUSTOM_CLASS_PREFIX)) {
100            puffContainerClassList.remove(className);
101          }
102        });
103        puffContainerClassList.add(customClass);
104
105        // Get the custom style sheet (or create a new) that will reference the custom class.
106        const style = getCustomStyle();
107        // Add a rule for widths of at least BREAKPOINT pixels that sets the height to the
108        // biggest height
109        style.textContent +=
110           `@media screen and (min-width: ${BREAKPOINT}px) {
111              .${customClass} ${CARD_BODY_SELECTOR} {
112                height: ${maxHeight}px !important;
113              }
114            }`;
115      }
116    });
117  };
118
119  /**
120   * Event handler that removes any previous custom styles set with this script
121   * and then calls setPuffsToSameHeight().
122   */
123  const readjustPuffHeights = () => {
124    // We need to remove the custom style sheet to prevent the page being flooded
125    // with style elements.
126    removeCustomStyle();
127    setPuffsToSameHeight(PUFF_SELECTOR, CARD_SELECTOR);
128  };
129
130  // Adjust puff heights on window resize
131  window.addEventListener('resize', readjustPuffHeights);
132
133  // Adjust puff heights immediately, but wait for fonts to load first to avoid measuring with
134  // fallback fonts (which would yield incorrect heights).
135  document.fonts.ready.then(() => {
136    setPuffsToSameHeight(PUFF_SELECTOR, CARD_SELECTOR);
137  });
138})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.