the notorious
snacky

<- Quay về trang chủ

Vài ghi chép cho front-end developer

Thật ra đây là phần nội dung mà mình đã chia sẻ trong chương trình #30daysofsharing diễn ra khoảng đầu năm 2021, do bạn @Bác Làm Vườn trên WeBuild đề xuất. Mình chỉ copy ra đây những bài do mình viết.

Các bạn có thể vào repo này: https://github.com/congdv/30daysofsharing để đọc toàn bộ các bài viết của các thành viên khác.

Accessibility (A11y)

Một trang web đạt chuẩn accessibility khi nó được thiết kế và phát triển để giúp cho mọi người đều có thể sử dụng được, kể cả những người già, những người khuyết tật hay đơn giản là những người không sử dụng các thiết bị input thông thường trên máy tính.

Sử dụng được ở đây có nghĩa là:

Tất nhiên accessibility vẫn có thể đem lại rất nhiều lợi ích cho những người không mang khuyết tật, ví dụ keyboard navigation, hoặc người nào thị lực kém, vẫn chỉnh được chữ to lên, high constrast hơn, ai xài kết nối internet kém vẫn có thể sử dụng được trang web mà không gặp trở ngại.

Nếu chỉ có ý định support accessibility một cách cơ bản, bạn có thể focus vào các yếu tố sau:

Hiện tại, các hệ điều hành và các trình duyệt đữa đưa ra rất nhiều tiện ích để hỗ trợ accessibility, như là screen readers (đọc nội dung của trang web dựa vào các thuộc tính aria-*, trên MacOS có VoiceOver, trên Windows phải sử dụng các ứng dụng của bên thứ 3 như JAWS), hay các giải pháp điều khiển máy tính thông qua mắt nhìn (built-in của MacOS),... tất cả những giải pháp này đều phụ thuộc rất nhiều vào tiêu chuẩn WCAG.

Có thể tham khảo thêm các tài liệu sau đây về accessibility:

Bên cạnh đối tượng user là những người bình thường, lành lặn, đầy đủ cả tay chân tai mắt mũi mà chúng ta vẫn tưởng tượng ra hằng ngày, ngoài kia vẫn còn rất nhiều người kém may mắn hơn, và họ vẫn có điều kiện để tiêp xúc với công nghệ mỗi ngày, và những user như họ cần được hỗ trợ nhiều hơn từ phía những người trực tiếp làm ra sản phẩm, là frontend developer chúng ta, cho nên, hãy bỏ chút thời gian và công sức để giúp đỡ những user đặc biệt này, mình chắc chắn là bạn sẽ thấy công việc của mình có nhiều ý nghĩa hơn.

Đặc biệt, đối với những frontend developer đang làm việc tại Mỹ, thì luật pháp quy định mọi trang web đều phải accessible, nên không support hoặc không quan tâm đến a11y có thể coi là phạm pháp.

CSS Variable

Let's talk about CSS Variables.

CSS Variables, hay còn gọi là CSS Custom Properties, là các thuộc tính CSS có dạng --*, có chức năng lưu giữ các giá trị trong CSS.

Các giá trị này khi cần, có thể được đọc ra bằng hàm var(), giá trị trả về từ hàm var() này có thể được dùng để gán cho các CSS Properties khác.

// define
--foo: #ff0000;

// read and reassign
color: var(--foo);
--new-foo: var(--foo);

CSS Variables là case sensitive, nên --foo-bar sẽ khác với --Foo-bar và khác luôn với --foo-Bar.

Tính kế thừa của CSS variables thì giống với mọi properties khác nên không có gì để nói đến. Có thể hiểu nôm na là, một CSS variable được define/hay thay đổi trong một scope, thì mọi scope con của nó đều có thể access đến giá trị của variable đó.

Nếu chúng ta sử dụng hàm var() để lấy giá trị của một CSS variable chưa được define, thì có thể bỏ thêm một tham số vào để làm fallback value, ví dụ:

var(--something-not-defined, 10) // giá trị sẽ là 10

--something-defined: 50;
var(--something-defined, 10) // giá trị sẽ là 50

Lưu ý, nếu browser không hỗ trợ CSS Variable thì xài fallback value cũng vô dụng.

Một case quan trọng nhất đó là, sẽ thế nào nếu bạn sử dụng sai kiểu dữ liệu cho một variable? Ví dụ một variable có giá trị số, nhưng gán vào cho một property có giá trị màu (như color, hay background-color)?

Ví dụ:

:root { --text-color: 16px; }
p { color: blue; }
p { color: var(--text-color); }

Ở đây biến --text-color mang giá trị số (16px) nhưng được gán cho thuộc tính color của thẻ <p>. Là sai lè lè.

Browser nó sẽ làm như này (#1):

  1. Đầu tiên, nếu gặp giá trị không hợp lệ, nó sẽ lấy giá trị inherit, tức là giá trị được kế thừa từ level cao hơn. Nhưng thẻ <p> này không có style nào được set ở level cao hơn.
  2. Nên nó sẽ lấy giá trị initial mặc định, là màu đen.

Cuối cùng, giá trị trả về của var(--text-color) trong trường hợp này sẽ là màu đen (có thể nói CSS cũng có type inference ). Nên chốt lại, màu chữ của thẻ <p> trong trường hợp này sẽ là màu đen.

Not so typed

Let's talk about Array sorting.

Khác với các ngôn ngữ định kiểu (typed language), Array trong JS có thể chứa nhiều phần tử với nhiều kiểu dữ liệu khác nhau. Vì đặc tính này, các hàm như Array.prototype.sort buộc phải chuyển từng phần tử về dạng chuỗi (string) khi sort, nên chúng ta sẽ có nhưng pha sort thần sầu như này:

let a = [1, 4, 3, 10, 11];
a.sort()
Expected:
	a = [1, 3, 4, 10, 11]
Actual:
	a = [1, 10, 11, 3, 4]

Mảng a ở trên được sort theo thứ tự alphabet, mặc dù input của chúng ta là một mảng chỉ chứa toàn kiểu số.

TypeScript cũng không handle được case này.

let a: Number[] = [1, 4, 11, 3, 10];
a.sort(); // [1, 10, 11, 3, 4]

Cách giải quyết tất nhiên là phải viết custom comparator cho hàm sort:

a.sort((a, b) => a - b);

Nhìn qua thì có vẻ chẳng có gì để bàn cãi, nhưng đây thực sự là một hành động dư thừa không đáng có, khi một thao tác đơn giản như sort một mảng số theo thứ tự tăng dần, mà chúng ta cũng phải viết custom comparator cho nó.

Một cách giải quyết khác "đẹp" hơn, đó là sử dụng Typed Array. Ở đây, chúng ta assume các số trong mảng cần sắp xếp là các giá trị integer 32 bit:

let ta = new Int32Array([1, 4, 11, 3, 10]);
ta.sort(); // [1, 3, 4, 10, 11]

Sau khi xử lý dữ liệu với typed array xong, thì cần phải chuyển nó về lại dạng Array (vì không phải chức năng nào của Array cũng có trong TypedArray, ví dụ, không có push, pop, hàm isArray() cũng sẽ trả về false,...).

let a = [...ta];
// hoặc
let a = Array.from(ta);

Quick note về Origin trong JavaScript

Một URL thường sẽ có cấu tạo như sau:

  <scheme>://<host>:<port>/<path>

  ví dụ

  http://localhost:45848/hello
  https://notes.huy.rocks

Một tập hợp của scheme, hostport sẽ định nghĩa thành một origin. Vậy cho nên khi nói hai nội dung có cùng origin tức là chúng nằm trên cùng scheme + host + port, và tất nhiên nếu một trong 3 yếu tố trên khác nhau thì chúng ta có 2 nội dung không nằm cùng origin với nhau.

Riêng IE, với phong cách nổi loạn thường thấy, sẽ bỏ qua port khi xét origin, nên 2 URL có cùng scheme + host mà khác port thì vẫn tính là same origin.

Ví dụ về 2 URL có cùng origin:

  https://something.com/hello
  https://something.com/yolo

Ví dụ về các URL không có cùng origin với nhau:

  // Khác scheme
  http://abc.com/bobo
  https://abc.com/koko

  // Khác host
  https://foo.abc.com
  https://bar.abc.com

  // Khác port
  https://abc.com
  https://abc.com:8443

Phân biệt được sự khác nhau về origin có thể giúp bạn hiểu và tìm ra giải pháp dễ dàng hơn khi gặp những vấn đề liên quan đến CORS (cross-origin resource sharing), khi truy xuất localStorage, hay khi tìm hiểu về execution context, event loop,...

Đối với các thao tác liên quan đến network connection trên một trang web (HTTP request, hay load image), việc truy xuất cross-origin cho các thao tác sau đây được cho phép:

Đối với Cookie, thì khác subdomain vẫn được tính là cùng origin.