All about table
- tutorials
- (Updated at )
當被問到某個頁面或是活動案要需要多久時間,多數我會回答看頁面怎麼製作的,其中一個影響的因素即是是否有使用表格,及如何使用表格。 圖形使用界面的網頁開發軟體都支援直接畫表格,但不適當的使用方式,相對也多產生了不必要的處理時間。當然這樣的問題不單只有表格還有許多的因素,但本篇先著重在表格的問題。表格本身並沒有問題,而是不當的使用壞了他的名聲。
表格應用上常見的問題
錯誤的連結標籤位置
<table>
<tr>
<td><a href="#link1">cell1</a></td>
<a href="#link2"><td>cell2</td></a>
</tr>
<a href="#link3">
<tr>
<td>cell3</td>
<td>cell4</td>
</tr>
</a>
</table>
cell1 | cell2 |
cell3 | cell4 |
這個表格有三個連結標籤的表現方式,但其中只有 #link1 是正確的,其餘二個是錯誤的。#link2,#link3 分別想達到,整個 <td>
及整列 <tr>
都可以點擊,而非只有欄位中的內容才能點擊。
首先 <tr>
的角色是 <td>
的容器,用來表達不同的 <td>
之間的關係,算是一種無實體不可視的標籤,所以在 <table>
、<tr>
、<td>
之間不應該出現任何內容,所有錯誤的內容都會被移出,如果你試著在中間中輸入任何文字,會發現這些文字會出現在表格式外,這是因為在正確的 DOM Tree 中,會是下面的架構。
你或許會問如果這是錯誤的用法,那麼顯示的 html 結果也是不正確的,那麼怎麼還會發生有人錯誤使用的問題。這就要歸咎於 ie 了,因為僅有 ie 可以容許這樣的寫法。
正確的做法應該使用 onClick event,同樣的表格加上了 onclick 事件處理。
<table>
<tr>
<td><a href="#link1">cell1</a></td>
<td onclick="alert('#link2');">cell2</td>
</tr>
<tr onclick="alert('#link3');">
<td>cell3</td>
<td>cell4</td>
</tr>
</table>
cell1 | cell2 |
cell3 | cell4 |
當然依所要達到的結果不同,也可以使用 Javascript 來處理這些事件,可以讓 html 的內容更為乾淨更容易瀏灠。
$('td').click(function() {
console.log('td clicked!');
});
$('tr').click(function() {
console.log('tr clicked!');
});
當然在實務上,你會需要更多的條件或參數來輔助,上面的範例同時對 <tr>
、<td>
都做了 click 事件處理,而 <tr>
是 <td>
的容器,所以點擊任何一個 <td>
,都同時觸發該 <td>
所屬的 <tr>
的點擊事件,也就是 td clicked!
及 tr clicked!
兩個結果都會出現。當然這也包含 <td>
中的內容,點擊 #link1 也同樣都會觸發兩個 click 事件。
我們可以加上不同 css 類別來區分不同的目標元件,再以 data
標籤來傳遞所需要的參數。
<table>
<tr>
<td><a href="#link1">cell1</a></td>
<td class="cell" data-link="#link2">cell2</td>
</tr>
<tr class="row" data-link="#link3">
<td>cell3</td>
<td>cell4</td>
</tr>
</table>
<script>
$('td.cell').click(function() {
console.log('td:' + $(this).data('link'));
});
$('tr.row').click(function() {
console.log('tr:' + $(this).data('link'));
});
</script>
經過這樣分類之後,就不會有重複的事件處理,如此一來點擊 cell3 或 cell4 會只會顯示 tr:#link3
,而點擊 cell2 則顯示 td:#link2
,當然點了 cell1 就直接連結到指定的 #link1 連結。
Missing ul, ol tag
同樣的規則也存在 <ul>
、<ol>
、<dl>
等條列式標籤中。任何的 <li>
或 <dd>
都不應該獨立存在,一定要包含在 <ul>
、<ol>
、<dl>
之中,所有的內容也該只能存在 <li>
之內,<ul>
、<ol>
、<dl>
、<li>
之間不應該有任何其他內容。
這些也是常見錯誤:
a 放於錯誤的位置
<ul>
<a href="#"><li>list1</li></a>
</ul>
缺少 ul, ol 標籤
<li>list1</li>
<li>list2</li>
<li>list3</li>
避免過於複雜的表格
有時因為資料的特性,表格也會相較的複雜而使用了 colspan
、rowspan
屬性來合併數個 <td>
變成跨欄或跨列的欄位,在以顯示資料為主的表格。不過這邊要提的並不是指資料表格,而是做為 layout 使用的表格。
的確在 css3 的時代,已不應該再使用 <table>
來做 layout 使用,但只要使用得宜並非不可以,況且仍有許多頁面仍使用 Dreamweaver 來畫表格,或使用 Photoshop 來直接輸出 html,都會出現這樣的問題。還有一個情況只能使用 <table>
的是 edm,因為大部份線上的 Email 系統會過濾信件內文,所以都不完整的支援 css,不同的平台過濾條件的差異性也不同,這種時候只有 <table>
是唯一的選擇。
分割成數個 Table
<table>
<tr>
<td colspan="7"></td>
</tr>
<tr>
<td colspan="7"></td>
</tr>
<tr>
<td colspan="7"></td>
</tr>
<tr>
<td colspan="3" rowspan="4"></td>
<td colspan="4"></td>
</tr>
<tr>
<td colspan="4"></td>
</tr>
<tr>
<td colspan="4"></td>
</tr>
<tr>
<td colspan="4"></td>
</tr>
<tr>
<td colspan="7"></td>
</tr>
<tr>
<td colspan="7"></td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
</table>
1 | 2 | 3 | 4 | 5 | 6 |
這種狀況很常見,只有最後一列需要切成數欄,所以其他列便設定了 colspan
來合併成一欄,之後某個原因最後一列刪除了一欄,你也會需要更改其他列的 colspan
或是根本就忘了,因為 colspan
已超過最多欄數,所以顯示的結果看起來會相同。
把握一個簡單的原則,當需要合併的欄數或列數超過需要清點,或是使用的次數超過數次,這時候就應該要把一個大表格,拆成數個小表格使用。
<table>
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr>
<td>
<table width="100%">
<tr>
<td width="50%"></td>
<td width="50%">
<table width="100%">
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr>
<td>
<table width="100%">
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
</tr>
</table>
</td>
</tr>
</table>
|
|||||||||
|
把比較複雜或是可能會造成其他欄位需要合併的狀況的部份,另外製作成獨立的表格,原本的一個大表格,修改後則會變成一個大表格與三個小表格組成,乍看 html 變長了但彼此之間的相依性降低了,修改的彈性反而增加了許多,不用再擔心修改了某個欄位是否會影響其他欄位。
避免過度分散內容
<table>
<tr>
<td><input type="radio"></td>
<td>Lorem ipsum</td>
</tr>
<tr>
<td colspan="2">Choose one or two options:</td>
<tr>
<tr>
<td></td>
<td>
<input type="checkbox"> option1<br>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="checkbox"> option2<br>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="checkbox"> option3<br>
</td>
</tr>
</table>
Lorem ipsum | |
Choose one or two options: | |
option1 |
|
option2 |
|
option3 |
這樣的問題不一定只發生在使用表格時,只是因為軟體的使用習慣加上不瞭解 DOM 的關係,比較常發生在表格上。首先來提比較容易理解,但通常被忽略的 UI (User Interface)。當在使用 radio, checkbox 請務必配合 <label>
使用,這樣使用者在點擊時,只要點了相關的文字即可,不需要一定要點到 radio, checkbox 本身,除了這是一個直覺反應之外,通常選項的文字範圍也會比 radio, checkbox 本身大的許多而容易點擊。
因為在 radio 及該選項的文字,被區分在不同的 <td>
中,所以 <label>
必需要分開設定,所以你需要這麼做。
<tr>
<td><input type="radio" id="option1"></td>
<td><label for="option1">Lorem ipsum</label></td>
</tr>
當然這樣是正確的語法,也是正確的架構沒有錯,但請想像當表單上有數十個這樣的選項,你需要為每一個選項指定 id 名稱,請注意 id 是唯一值不可重複 (這也是常犯錯誤之一),每個選項需要指定 id 名稱兩次,這是個無聊又煩索的工作。
<tr>
<td></td>
<td><label><input type="radio"> Lorem ipsum</label></td>
</tr>
當相關的元件擺在一起時,只要簡單直接用 <label>
包住,則完成完全相同的效果,請看這樣省下了多少 html 代碼所使用的空間以及指定 id 名稱的時間。
再進一步,我們來看 checkbox 的部份,假設我們限制至少需要勾選一項,最多不可超過二項,檢查的部份只能使用 Javascript。說明方便起見,我們先把原先的 html 加上需要的名稱。
<tr>
<td></td>
<td>
<label><input type="checkbox" name="question[]" value="1"> option1</label><br>
</td>
</tr>
<tr>
<td></td>
<td>
<label><input type="checkbox" name="question[]" value="2"> option2</label><br>
</td>
</tr>
<tr>
<td></td>
<td>
<label><input type="checkbox" name="question[]" value="3"> option3</label><br>
</td>
</tr>
指定元件名稱為 "question[]",因為 checkbox 是複選選項,在 Javascript, PHP 的命名習慣上,這樣會使得該變數在表單送出時,以陣列的方式存在來達到複數選項的目的。再以 Javascript 程式來檢查 "question[]" 的數量是否符合我們設定的條件。
// 找尋所有 input 元件,其類型為 checkbox 並且名稱為 "question[]",同時是已選取的項目
var $questionElements = $("input[type=checkbox][name='question[]']:checked");
if ($questionElements.length < 1 || $questionElements.length > 2 ) {
// 找到的項目的數量不足 1 或是大於 2
// 取消表單送出,顯示提示回饋
return false;
}
html 的架構算單純,檢查的程式也很簡單啊,你說。同樣的請想像你有數個這樣的內容,以及限制的數量條件或許不同,更或許有許多表單,那麼這段程式需要重複的出現多少次呢? 嗯哼!? Let's make it better, shall we?
<tr>
<td>
<p>Question 1:</p>
<div class="checkbox-group" data-min="1" data-max="2">
<label><input type="checkbox" name="question[1][]" value="1"> option1</label><br>
<label><input type="checkbox" name="question[1][]" value="2"> option2</label><br>
<label><input type="checkbox" name="question[1][]" value="3"> option3</label><br>
<label><input type="checkbox" name="question[1][]" value="4"> option4</label><br>
</div>
</td>
</tr>
<tr>
<td>
<p>Question 2:</p>
<div class="checkbox-group" data-min="2" data-max="5">
<label><input type="checkbox" name="question[2][]" value="1"> option1</label><br>
<label><input type="checkbox" name="question[2][]" value="2"> option2</label><br>
<label><input type="checkbox" name="question[2][]" value="3"> option3</label><br>
<label><input type="checkbox" name="question[2][]" value="4"> option4</label><br>
<label><input type="checkbox" name="question[2][]" value="5"> option5</label><br>
<label><input type="checkbox" name="question[2][]" value="6"> option6</label><br>
</div>
</td>
</tr>
同樣的還是保留了 <table>
相關的代碼在範例中。將不同的問題分別再包在另一個 <div>
中,並給予 "checkbox-group" 類別名稱,再分別設定所需要的其他參數,data-min
表示最少需選取項目, data-max
表示最多可選取項目。適當的將相關的屬性的項目放在一起,html 更容易閱讀相關的程式的撰寫也更單純。
// 先假設檢查會是通過的
var valid = true;
$(".checkbox-group").each(function() {
var $group = $(this);
var min = parseInt($group.data('min'), 10);
var max = parseInt($group.data('max'), 10);
var $elements = $group.find("[type=checkbox]:checked");
if ($elements.length < min || $elements.length > max) {
// 一互有發現不符合條件的,則設定檢查狀況是失敗的
valid = false;
// 脫離 $(".checkbox-group").each() 這個迴圈
// 不再往下做其他項目的檢查
return false;
}
});
if (!valid) {
// 找到的項目的數量不符合條件
// 取消表單送出,顯示提示回饋
return false;
}
先別害怕,程式如何撰寫並不是這裡的重點,這段程式與上面 html 所做的分類的邏輯完全相同。逐一對 .checkbox-group
類別的項目,依指定的 min, max 數值,檢查底下的 checkbox 選取的數量是否符合。注意到程式中完全沒有出現 checkbox 的名稱嗎? 因為在檢查的過程中,我們只要知道對象群組及條件,並不需要知道對象個別是誰,這段程式不但不需要先前的重複複製、貼上再修改名稱及數量,而且也容易重複使用不需要每次重新撰寫,即使選項的條件變更,也只需要修改 html 中的設定,不需要修改程式,前提當然是有 html 正確組織及配合的參數命名方式。
垃圾進垃圾出
正確的組織不在於語法的正確與否,也不是使用了 <div>
就比較高等,用 <table>
就是錯誤,而是在於將內容依屬性適當的群組化,這樣不但讓 html 代碼易讀性提高,需要修改時或是添加 css 設定時,都會更清楚及容易,當然有邏輯的分類也才能夠撰寫出良好的程式。
再一個表單的例子。有一批產品的資料,包含有圖片,名稱及簡短說明文字。這是一個非常常見的糟糕架構,牽扯到的層面也比較多一點,接下來會分別提到 html, css, php 三個部份。
<table>
<tr>
<td>img1</td>
<td>img2</td>
<td>img3</td>
<td>img4</td>
</tr>
<tr>
<td>name1</td>
<td>name2</td>
<td>name3</td>
<td>name4</td>
</tr>
<tr>
<td>text1</td>
<td>text2</td>
<td>text3</td>
<td>text4</td>
</tr>
<tr><td colspan="4"></td></tr>
<tr>
<td>img5</td>
<td>img6</td>
<td>img7</td>
<td>img8</td>
</tr>
<tr>
<td>name5</td>
<td>name6</td>
<td>name7</td>
<td>name8</td>
</tr>
<tr>
<td>text5</td>
<td>text6</td>
<td>text7</td>
<td>text8</td>
</tr>
<tr><td colspan="4"></td></tr>
<tr>
<td>img9</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>name9</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>text9</td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
img1 | img2 | img3 | img4 |
name1 | name2 | name3 | name4 |
text1 | text2 | text3 | text4 |
img5 | img6 | img7 | img8 |
name5 | name6 | name7 | name8 |
text5 | text6 | text7 | text8 |
img9 | |||
name9 | |||
text9 |
要由程式產生表格,需要定義或計算表格的欄數及列數,照指定的順序輸出欄位及其內容,輸出的欄位數不滿一列定義欄位數時,需輸出空的的欄位,每完成一列輸出,依未輸出資料數判斷是否要產生間隔列。
比較複雜的部份在於如何依指定的順序輸出,因為這個表格並非每次處理一項產品,而是每項產品要分次輸出其內容。先依順輸出四項產品的 image
欄位,再於次列輸出 name
欄位,再次列輸出 description
欄位,至此完成四項產品的之後,再處理下四筆資料。
<table>
<?php
// 假設的資料來源
$items = [
['image' => 'img1', 'name' => 'name1', 'description' => 'text1'],
['image' => 'img2', 'name' => 'name2', 'description' => 'text2'],
['image' => 'img3', 'name' => 'name3', 'description' => 'text3'],
['image' => 'img4', 'name' => 'name4', 'description' => 'text4'],
['image' => 'img5', 'name' => 'name5', 'description' => 'text5'],
['image' => 'img6', 'name' => 'name6', 'description' => 'text6'],
['image' => 'img7', 'name' => 'name7', 'description' => 'text7'],
['image' => 'img8', 'name' => 'name8', 'description' => 'text8'],
['image' => 'img9', 'name' => 'name9', 'description' => 'text9'],
];
// 設定每列顯示的產品數量
$numberOfItemsPerRow = 4;
// 稍後在輸出產品內容時對應用的名稱
$keysForItem = array('image', 'name', 'description');
// 取得產品資料的欄位筆數,供稍後使用,本範例中是 3
$numberOfValuesPerItem = count($keysForItem);
// 取得資料總筆數,本範例中是 9
$totalItems = count($items);
// 以無條件進位計算表格的總列數,本範例中是 ceil(9 / 4) = 3
$totalRows = ceil($totalItems / $numberOfItemsPerRow);
// 用來讀取產品資料使用的索引
$index = 0;
// html 是由上到下,所以第一個迴圈會是需要輸出的列數的次數
// 程式一般會由 0 開始計算,所以這裡的 $i 的結果分別會是 0, 1, 2 三次
for ($i = 0; $i < $totalRows; $i++) {
// 每項產品要輸出的欄位次數,$j 的結果分別是 0, 1, 2 三次
for ($j=0; $j < $numberOfValuesPerItem; $j++) {
// 計算產品資料索引,這裡會執行了兩個迴圈的次數,也就是 3 x 3 = 9 次
// 每次起始會是該產品列的第一項產品,所以 $index 的結果會是
// ($i = 0) 0, 0, 0
// ($i = 1) 4, 4, 4
// ($i = 2) 8, 8, 8
$index = $i * $numberOfItemsPerRow;
// 取得本次要輸出產品資料的欄位名稱,$key 所有結果會是
// ($i = 0) image, name, description
// ($i = 1) image, name, description
// ($i = 2) image, name, description
$key = $keysForItem[$j];
echo "<tr>";
// 由左到右,開始準備輸出每個表格欄位
// 每列產品數,$z 的結果分別是 0, 1, 2, 3
// 這裡一共會執行 3 x 3 x 4 = 36 次
for ($z=0; $z < $numberOfItemsPerRow; $z++) {
// $index 索引分別在 $i, $j, $key 時的所有結果
// ($i = 0, $j = 0, $key = image) 0, 1, 2, 3
// ($i = 0, $j = 1, $key = name) 0, 1, 2, 3
// ($i = 0, $j = 2, $key = description) 0, 1, 2, 3
// ($i = 1, $j = 0, $key = image) 4, 5, 6, 7
// ($i = 1, $j = 1, $key = name) 4, 5, 6, 7
// ($i = 1, $j = 2, $key = description) 4, 5, 6, 7
// ($i = 2, $j = 0, $key = image) 8, 9, 10, 11
// ($i = 2, $j = 1, $key = name) 8, 9, 10, 11
// ($i = 2, $j = 2, $key = description) 8, 9, 10, 11
if (isset($items[$index])) {
// 若有產品資料時,則輸出第 $index 筆資料,欄位名稱為 $key 的內容
echo "<td>" . $items[$index][$key] . "</td>";
}
else {
// 若無資料,則輸出空格
echo "<td></td>";
}
// 索引累加 1
$index++;
}
echo "</tr>";
}
// 每輸出完一輪產品列,即 4 項產品,判斷是否要輸出間隔用的空列
// $index 分別會是 3, 7, 11
if ($index < $totalItems) {
echo '<tr><td colspan="' . $numberOfItemsPerRow. '"></td></tr>';
}
}
?>
</table>
如果你不熟悉寫程式,這段內容會看起來複雜許多,但幾個小時過後我也會不容易看懂。因為這段程式的邏輯是為了產生表格,而不是一般在處理資料時的邏輯,因此幾乎每次都會需要花點時間重看程式的做法,才會知道該段程式的目的。如何撰寫程式並不是本篇主題,但應仍可發現主要在輸出 html 內容的程式只有 5 行,並且迴圈總計跑了 36 次,而這個範例只有 9 筆資料。
錯誤的例子看完了,那麼就來看正確應該怎麼做。雖然上面已經提過 <table>
並不適合拿來做排版,但是這篇主要還是以 <table>
為主的文章,所以還是會使用 <table>
但是會使用在正確的地方。
<div class="items-wrapper">
<div class="item-box">
<table>
<tr><td>image</td></tr>
<tr><td>name</td></tr>
<tr><td>text</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>image</td></tr>
<tr><td>name</td></tr>
<tr><td>text</td></tr>
</table>
</div>
</div>
首先我們拉好所以需要的 html 架構,每項產品的資料放置於 <table>
內,外面再以 div.item-box
包住,這樣可以讓架構更清楚一點,也容易使用 css 來做其他的樣式排版,是不是簡單又清楚許多呢?
<div class="items-wrapper">
<?php foreach($items as $item): ?>
<div class="item-box">
<table>
<tr><td><?php echo $item['image']; ?></td></tr>
<tr><td><?php echo $item['name']; ?></td></tr>
<tr><td><?php echo $item['description']; ?></td></tr>
</table>
</div>
<?php endforeach; ?>
</div>
加上程式之後幾乎還是維持原 html 的內容,程式部份只有 5 行,其中 3 行是輸出資料,顯示會輸出的內容即使是看原始碼也一目瞭然。因為我們只需要在對應的地方顯示資料,而不需要由程式來控制排版或 html 的輸出,瀏灠器直接開啟頁面,或是經過其他編輯軟體預覽時,也不會因為無法正確執行 php 程式而造成頁面破碎的問題,接下來我們只要再使用 css 來控制我們想要的排版即可。
.items-wrapper {
width:100%;
}
.items-wrapper:after {
content:"";
display:table;
clear:both;
}
.item-box {
width:25%;
float:left;
margin-bottom:10px;
}
簡單的設定以 .items-wrapper
為最大可用寬度,.item-box
每項可使用的寬度為其父元件 .items-wrapper
寬度的 25% 並向左靠齊,這樣可以達到每排 4 項產品,若要調整每排的數量,只要調整可使用的寬度即可,例如改為 33% 則每排三項。 margin-bottom
用來取代 <tr><td colspan="4"></td></tr>
每排的間隔。 最後,因為 .item-box
是往左靠齊的,所以當產品項目沒有剛好填滿整個 .items-wrapper
空間時,在這之後的內容仍會因為置左的影響而跑上來,:after
pseudo 的部份則正是讓置左的效果失效。
最後的結果及 html 原始碼會像這樣
<div class="items-wrapper">
<div class="item-box">
<table>
<tr><td>img1</td></tr>
<tr><td>name1</td></tr>
<tr><td>text1</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img2</td></tr>
<tr><td>name2</td></tr>
<tr><td>text2</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img3</td></tr>
<tr><td>name3</td></tr>
<tr><td>text3</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img4</td></tr>
<tr><td>name4</td></tr>
<tr><td>text4</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img5</td></tr>
<tr><td>name5</td></tr>
<tr><td>text5</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img6</td></tr>
<tr><td>name6</td></tr>
<tr><td>text6</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img7</td></tr>
<tr><td>name7</td></tr>
<tr><td>text7</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img8</td></tr>
<tr><td>name8</td></tr>
<tr><td>text8</td></tr>
</table>
</div>
<div class="item-box">
<table>
<tr><td>img9</td></tr>
<tr><td>name9</td></tr>
<tr><td>text9</td></tr>
</table>
</div>
</div>
<p>end of products</p>
img1 |
name1 |
text1 |
img2 |
name2 |
text2 |
img3 |
name3 |
text3 |
img4 |
name4 |
text4 |
img5 |
name5 |
text5 |
img6 |
name6 |
text6 |
img7 |
name7 |
text7 |
img8 |
name8 |
text8 |
img9 |
name9 |
text9 |
end of products
Please
Being a web designer you may not need to expert at javascript or php, but still it's your job to make properly html and css contents, otherwise you are just a graphic designer or people who knows photoshop, it's the same as web developers. And most of these problems are not technical but it cause technical issues which should be prevent from the begining.