Używając CSS, jak „ściśle” wyrównać „A” na dole / końcu „B”, gdzie „B” ma nieznaną szerokość i zawijanie tekstu

9

Mam „sortowalną” tabelę, w której nagłówek aktualnie posortowanej kolumny wyświetla ikonę:

wprowadź opis zdjęcia tutaj

Ikona sortowania ma być wyświetlana na końcu tekstu (tzn. Obsługujemy LTR / RTL). Obecnie używam display:flex. Jeśli jednak szerokość tabeli się zmniejszy, a tekst nagłówka kolumny zacznie się zawijać, wpadam w dwuznaczne stany, w których nie jest jasne, która kolumna jest posortowana:

wprowadź opis zdjęcia tutaj

Zamiast tego chciałbym spełnić następujące wymagania:

  1. Ikona jest zawsze „ściśle” wyrównana z „końcem” najdłuższej linii tekstu (nawet jeśli owijająca komórka / przycisk jest szersza).
  2. Ikona powinna być również wyrównana do dołu z ostatnim wierszem tekstu.
  3. Ikona nigdy nie powinna się owijać we własną linię.
  4. Przycisk musi być obecny i powinien obejmować całą szerokość komórki. (Jego wewnętrzna stylizacja i znaczniki mogą się zmieniać w razie potrzeby).
  5. CSS i HTML tylko, jeśli to w ogóle możliwe.
  6. Nie może polegać na znanych / ustawionych szerokościach kolumn lub tekście nagłówka.

Na przykład:

wprowadź opis zdjęcia tutaj

Byłem eksperymentować z wieloma różnymi kombinacjami display: inline/inline-block/flex/grid, position, ::before/::after, a nawet float(!), Ale nie można uzyskać pożądanego zachowania. Oto mój obecny kod demonstrujący problem:

.table {
  border-collapse: collapse;
  width: 100%;
}

.thead {
  border-bottom: 3px solid #d0d3d3;
}

.thead .tr {
  vertical-align: bottom;
}

.button {
  padding: 1rem;
  text-align: start;
  font-family: Arial, "noto sans", sans-serif;
  font-size: 0.875rem;
  border: 0;
  background-color: transparent;
  width: 100%;
  display: flex;
  align-items: flex-end;
  font-weight: bold;
  line-height: 1.4;
}

.sort {
  width: 1.25rem;
  height: 1.25rem;
}
<table class="table">
  <thead class="thead">
    <tr class="tr">
      <th class="th">
        <button class="button">
          Age
          <span class="sort"></span>
         </button>
      </th>
      <th class="th">
        <button class="button">
          Favorite Food
          <span class="sort"></span>
        </button>
      </th>
      <th class="th" aria-sort="ascending">
        <button class="button">
          Favorite Color
          <span class="sort">
            <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.25rem; height: 1.25rem;"> <path d="M11.4709 4.2136C11.7638 3.92071 12.2386 3.92071 12.5315 4.2136L17.1277 8.8098C17.4206 9.10269 17.4206 9.57756 17.1277 9.87046C16.8348 10.1633 16.3599 10.1633 16.0671 9.87046L12.7512 6.55459V19.25C12.7512 19.6642 12.4154 20 12.0012 20C11.587 20 11.2512 19.6642 11.2512 19.25V6.55459L7.93533 9.87046C7.64244 10.1633 7.16756 10.1633 6.87467 9.87046C6.58178 9.57756 6.58178 9.10269 6.87467 8.8098L11.4709 4.2136Z"> </path></svg>
          </span>
        </button>
      </th>
      <th class="th">
        <button class="button">
          Likes Neil Diamond?
          <span class="sort"></span>
        </button>
      </th>
    </tr>
  </thead>
</table>

Wszelkie pomysły, jak zrealizować ten interfejs? Prawdopodobnie ma to niewiele wspólnego ze stołem lub przyciskami. Naprawdę potrzebuję Thing A„ciasno” wyrównanego do dołu / końca Thing B(o elastycznej szerokości, który może zawijać tekst), ale Thing Anie mogę zawijać się na własnej linii.

Próbowałem zadzierać z flexwartościami, ale dowolna kombinacja powoduje, że tekst zawija się przedwcześnie lub nie jest wystarczająco wcześnie.

robertwbradford
źródło
Próbowałem naprawić problem z bieżącym css i html, jednak przycisk i element span łączyły się tam, gdzie
odczuwałem
@stanchacon dzięki za spojrzenie. Powinienem był wspomnieć, ale nie będę miał luksusu ustawiania szerokości kolumn ani wiedzy o tym, co będzie w nagłówku. Dodałem # 6 do wymagań.
robertwbradford
1
@stanchacon ... i mógłbym teraz wybrać się na ribeye :)
robertwbradford
@robertwbradford Czy to już jest rozwiązane? Czy nadal szukasz rozwiązania?
Furqan Rahamath,
@MohammedFurqanRahamathM, na to pytanie jeszcze nie ma odpowiedzi. Wciąż szukam ...
robertwbradford,

Odpowiedzi:

1

Myślisz, że potrzebujesz do tego JS. Oto odpowiedź, która powinna spełniać wszystkie warunki oprócz 5.

const debounce = fn => {
  let frame;

  return (...params) => {
    if (frame) {
      cancelAnimationFrame(frame);
    }
    frame = requestAnimationFrame(() => {
      fn(...params);
    });
  };
};

const text = [...document.querySelectorAll(".text")];
const iconWidth = "1.25rem";

const positionIcon = () => {
  if (!text) return;
  
  text.forEach(t => {
    const width = t.getBoundingClientRect().width;

    const icon = t.nextElementSibling;
    if (!icon) return;
    
    icon.style.left = `calc(${width}px + ${iconWidth})`;
  });
};

positionIcon();
window.addEventListener("resize", debounce(positionIcon));
.table {
  border-collapse: collapse;
  width: 100%;
}

.thead {
  border-bottom: 3px solid #d0d3d3;
}

.thead .tr {
  vertical-align: bottom;
}

.button {
  padding: 1rem;
  text-align: start;
  font-family: Arial, "noto sans", sans-serif;
  font-size: 0.875rem;
  border: 0;
  background-color: transparent;
  width: 100%;
  font-weight: bold;
  line-height: 1.4;
  position: relative;
}

.sort {
  width: 1.25rem;
  height: 1.25rem;
  position: absolute;
  bottom: 1rem;
  left: 100%; /* no js fallback */
}

.sort svg {
  height: 1.25rem;
  width: 1.25rem;
}
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
   <svg id="arrow" viewBox="0 0 24 24" role="presentation"> <path d="M11.4709 4.2136C11.7638 3.92071 12.2386 3.92071 12.5315 4.2136L17.1277 8.8098C17.4206 9.10269 17.4206 9.57756 17.1277 9.87046C16.8348 10.1633 16.3599 10.1633 16.0671 9.87046L12.7512 6.55459V19.25C12.7512 19.6642 12.4154 20 12.0012 20C11.587 20 11.2512 19.6642 11.2512 19.25V6.55459L7.93533 9.87046C7.64244 10.1633 7.16756 10.1633 6.87467 9.87046C6.58178 9.57756 6.58178 9.10269 6.87467 8.8098L11.4709 4.2136Z"> </path></svg>
</svg>
<table class="table">
  <thead class="thead">
    <tr class="tr">
      <th class="th">
        <button class="button">
          <span class="text">Age</span>
          <span class="sort">
            <svg>
              <use xlink:href="#arrow">
            </svg>
          </span>
         </button>
      </th>
      <th class="th">
        <button class="button">
          <span class="text">Favourite Food</span>
          <span class="sort">
          <svg>
              <use xlink:href="#arrow">
            </svg>
          </span>
        </button>
      </th>
      <th class="th" aria-sort="ascending">
        <button class="button">
          <span class="text">Favorite Color</span>
          <span class="sort">
            <svg>
              <use xlink:href="#arrow">
            </svg>
          </span>
        </button>
      </th>
      <th class="th">
        <button class="button">
          <span class="text">Likes Neil Diamond?</span>
          <span class="sort">
            <svg>
              <use xlink:href="#arrow">
            </svg>
          </span>
        </button>
      </th>
    </tr>
  </thead>
</table>

sol
źródło
1

Myślę, że nie ma tu żadnego problemu oprócz dwuznaczności wizualnej. Oto moje spostrzeżenia.

  • Rzeczy w rzeczywistości są ściśle związane. Domyślnie każdy element podąża za innym, chyba że określono inaczej.
  • Dlaczego są odstępy? Istnieją dwie odmiany tych
    • Po pierwsze: jeśli tekst nie znajduje się w żadnym elemencie zawijającym (stan, w którym jest teraz). W tym scenariuszu, ponieważ nie ma wyraźnie wymienionej szerokości, zarówno dla tekstu, jak i zakresu, istnieje automatyczne przypisanie szerokości i tekst próbuje uzyskać całą szerokość, z wyjątkiem szerokości 1,25 skraju, jaką przyjmuje element zakresu. I zgodnie z dokumentacją w3c, reguły istnieją tylko w celu identyfikacji punktów przerwania na danym ciągu, a nie odstępów, ponieważ justowanie tekstu jest zupełnie innym problemem. Proszę odnieść się do tego problemu, aby zrozumieć, jak to się robi, jest to standardowe pytanie dotyczące programowania dynamicznego badane na studiach. Oczywiście odstępy będą istnieć podczas justowania i pamiętaj o dostępnej szerokości, której przeglądarka nie zwinie.
    • Po drugie: Jeśli tekst znajduje się w elemencie zawijającym , może to być span, p, div itp. W tym przypadku również automatyczne przypisanie szerokości ma miejsce, ponieważ nie określono wyraźnie szerokości. I w tym przypadku można faktycznie sprawdzić w elemencie inspekcji, że element próbuje zająć całe miejsce, z wyjątkiem szerokości 1,25rem ikony sortowania.

Istnieje tak ścisłe ograniczenie, tyle że dynamiczne odstępy powodują wizualną niejednoznaczność.

Teraz, gdy dochodzę do rozwiązania, najbliższym rozwiązaniem, które mógłbym wymyślić bez interwencji JavaScript , chociaż nie jest idealne, jest:

  • Zawiń tekst w elemencie, być może w rozpiętość
  • Ogranicz szerokość tego tekstu, podając maksymalną szerokość:

    button.button > span {
        max-width: 60%; // I saw this to be decent enough
        text-align: center; // This is to offset the visual effect of spacing
    }
    

Uwaga: Ma text-align: centerto na celu zmniejszenie efektu brakującego miejsca, chyba że masz ścisły wymóg, aby nie iść z tym.

Daj mi znać, jeśli to rozwiąże twoje pytanie.

Tak wyglądałby efekt: wprowadź opis zdjęcia tutaj

Furqan Rahamath
źródło
Dziękuję za odpowiedź. W tym celu mamy wymóg, aby był na końcu.
robertwbradford
@robertwbradford Zmieniłem odpowiedź, wyjaśniając możliwy problem, z którym się spotkałeś i zaproponowałem rozwiązanie. Daj mi znać, jeśli to odpowie na twoje pytanie.
Furqan Rahamath,
0

Myślę, że jesteś prawie dobry i brakuje Ci (3), co jest nieco sztuczką. Pomysł polega na tym, aby element ikony miał szerokość równą 0i wyłączyć wszelkie spacje między tekstem a ikoną sortowania. W tym celu używam dodatkowego opakowania na tekst i usunąłem korzystanie z Flexbox.

Będziesz miał trochę przepełnienia, ale nie stanowi to problemu, ponieważ stosujesz padding. Możesz również zwiększyć, padding-rightjeśli chcesz

.table {
  border-collapse: collapse;
  width: 100%;
}

.thead {
  border-bottom: 3px solid #d0d3d3;
}

.thead .tr {
  vertical-align: bottom;
}

.button {
  padding: 1rem;
  text-align: start;
  font-family: Arial, "noto sans", sans-serif;
  font-size:0;
  border: 0;
  background-color: transparent;
  width: 100%;
  font-weight: bold;
  line-height: 1.4;
}
.button > span {
  font-size: 0.875rem;
}
.sort {
  width: 0;
  height: 1.25rem;
  display: inline-block;
  vertical-align: bottom;
}
<table class="table">
  <thead class="thead">
    <tr class="tr">
      <th class="th">
        <button class="button">
          <span>Age</span>
          <span class="sort"></span>
         </button>
      </th>
      <th class="th">
        <button class="button">
          <span>Favorite Food</span>
          <span class="sort"></span>
        </button>
      </th>
      <th class="th" aria-sort="ascending">
        <button class="button">
          <span>Favorite Color</span>
          <span class="sort">
            <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.25rem; height: 1.25rem;"> <path d="M11.4709 4.2136C11.7638 3.92071 12.2386 3.92071 12.5315 4.2136L17.1277 8.8098C17.4206 9.10269 17.4206 9.57756 17.1277 9.87046C16.8348 10.1633 16.3599 10.1633 16.0671 9.87046L12.7512 6.55459V19.25C12.7512 19.6642 12.4154 20 12.0012 20C11.587 20 11.2512 19.6642 11.2512 19.25V6.55459L7.93533 9.87046C7.64244 10.1633 7.16756 10.1633 6.87467 9.87046C6.58178 9.57756 6.58178 9.10269 6.87467 8.8098L11.4709 4.2136Z"> </path></svg>
          </span>
        </button>
      </th>
      <th class="th">
        <button class="button">
          <span>Likes Neil Diamond?</span>
          <span class="sort"></span>
        </button>
      </th>
    </tr>
  </thead>
</table>

Temani Afif
źródło
Dziękuję za odpowiedź. Kilka dobrych punktów, ale widzę, że nie spełnia on „wymogu 1”, w którym strzała musiałaby być wyrównana z końcem najdłuższego rzędu tekstu. W przypadku „Ulubionego koloru”, gdy „Kolor” jest zawijany do drugiej linii, ikona powinna wyrównać się poziomo z końcem „Ulubionego”, jak pokazano na ostatnim zrzucie ekranu.
robertwbradford,
1
@robertwbradford ah nie rozumiem tego w ten sposób .. Myślałem, że powinno być ściśle z ostatnim. Myślę, że nie masz szczęścia do spełnienia tego wymogu ...
Temani Afif,
0

jeśli twój tytuł thead nie jest dynamiczny, to mam bardzo łatwą poprawkę.

w tym celu musisz połączyć ze sobą ostatnie słowo tytułu i ikonę svg

np .: -html

<button>
 Long 

<span> title 
 <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.25rem; height: 1.25rem;"> <path d="M11.4709 4.2136C11.7638 3.92071 12.2386 3.92071 12.5315 4.2136L17.1277 8.8098C17.4206 9.10269 17.4206 9.57756 17.1277 9.87046C16.8348 10.1633 16.3599 10.1633 16.0671 9.87046L12.7512 6.55459V19.25C12.7512 19.6642 12.4154 20 12.0012 20C11.587 20 11.2512 19.6642 11.2512 19.25V6.55459L7.93533 9.87046C7.64244 10.1633 7.16756 10.1633 6.87467 9.87046C6.58178 9.57756 6.58178 9.10269 6.87467 8.8098L11.4709 4.2136Z"> </path></svg>
</span>
</button>

usuń display: flex z klasy .button i nadaj styl display: inline-block do dodanego zakresu

ze względu na to, że rozpiętość jest blokiem wbudowanym, ikona svg nigdy nie będzie w osobnej linii. przed nim będzie co najmniej jedno słowo

Shirish Maharjan
źródło
Dziękuję za odpowiedź.
Buduję
Możesz użyć Js, aby wstawić ostatnie słowo. To nie powinno być takie trudne
Shirish Maharjan,
0

Możesz spróbować w ten sposób:

<style>

.table {
  border-collapse: collapse;
  width: 100%;
}

.thead {
  border-bottom: 3px solid #d0d3d3;
}

.thead .tr {
  vertical-align: bottom;
}


.button {
  padding: 1rem;
  text-align: start;
  font-family: Arial, "noto sans", sans-serif;
  font-size: 0.875rem;
  border: 0;
  background-color: transparent;
    
 /* display: flex;
  align-items: flex-end;*/
  font-weight: bold;
  line-height: 1.4;
  float: left;
}
@media only screen and (min-width:502px)
{.button {
    width: calc(192px - 89px);
  }
  .button.food {
    width: calc(214px - 89px);
}
}

.sort {
  width: 1.25rem;
  height: 1.25rem;
  position: relative;
}
.sort svg {
  position: absolute;
}
</style>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <table class="table">
  <thead class="thead">
    <tr class="tr">
      <th class="th">
        <button class="button">
          Age
          <span class="sort"></span>
         </button>
      </th>
      <th class="th">
        <button class="button food">
          Favorite Food
          <span class="sort"></span>
        </button>
      </th>
      <th class="th" aria-sort="ascending">
        <button class="button">
          Favorite Color
          <span class="sort">
            <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.25rem; height: 1.25rem;"> <path d="M11.4709 4.2136C11.7638 3.92071 12.2386 3.92071 12.5315 4.2136L17.1277 8.8098C17.4206 9.10269 17.4206 9.57756 17.1277 9.87046C16.8348 10.1633 16.3599 10.1633 16.0671 9.87046L12.7512 6.55459V19.25C12.7512 19.6642 12.4154 20 12.0012 20C11.587 20 11.2512 19.6642 11.2512 19.25V6.55459L7.93533 9.87046C7.64244 10.1633 7.16756 10.1633 6.87467 9.87046C6.58178 9.57756 6.58178 9.10269 6.87467 8.8098L11.4709 4.2136Z"> </path></svg>
          </span>
        </button>
      </th>
      <th class="th">
        <button class="button">
          Likes Neil Diamond?
          <span class="sort"></span>
        </button>
      </th>
    </tr>
  </thead>
</table>

Mam nadzieję, że ta odpowiedź będzie dla ciebie pomocna. Nie stworzyłem css do widoku mobilnego!

Krishna Savani
źródło