Статьи

Как написать интересный список дел с помощью PHP и AJAX

В этом уроке Tuts + Premium на этой неделе мы будем работать со многими различными технологиями. В конечном итоге мы создадим список дел, который позволит вам или вашему пользователю создавать, обновлять и удалять элементы асинхронно. Для выполнения нашей задачи мы будем использовать возможности PHP и jQuery AJAX. Я думаю, вы обнаружите, что это не так сложно, как вы могли подумать. Я покажу вам, как именно!

Этот учебник включает в себя скринкаст, доступный для пользователей Tuts + Premium.



Как вы можете себе представить, мы не можем сохранять, удалять и обновлять записи в статической среде. Итак, мы должны создать базу данных MySql, в которой будет храниться информация.

Если вы используете PHPMyAdmin, откройте панель управления, перейдя по адресу http: // localhost / phpmyadmin.


В текстовом поле «Создать новую базу данных» введите «db» и нажмите «Создать». Далее вам нужно будет создать таблицу. Введите «todo» и «3» для «количество полей».


Теперь нам нужно добавить соответствующие столбцы.

  • id : уникальный идентификатор для идентификации каждой строки.
  • title : название нашей статьи
  • описание : описание, подробно описывающее, что нам нужно делать!

Убедитесь, что параметры каждого поля соответствуют параметрам, показанным на следующем рисунке.


Теперь, когда мы создали нашу базу данных, давайте быстро добавим несколько тестовых строк. Нажмите на вашу базу данных «db»; затем выберите «Обзор». Вы попадете на экран со списком содержимого каждой строки в вашей базе данных. Очевидно, этот раздел сейчас пуст. Выберите «Вставить» и добавьте несколько столбцов. Введите все, что вы хотите здесь.






Хотя это и не требуется никакими средствами, я считаю, что проще всего управлять своими функциями, группируя их в класс. Учитывая это, мы сейчас создадим класс «Db», который будет содержать несколько функций.

  • __construct : эта функция автоматически запускается, как только создается экземпляр объекта.
  • delete_by_id () : удаляет необходимую строку, передавая уникальный идентификатор строки.
  • update_by_id () : обновляет строку, передавая ее уникальный идентификатор.

Откройте выбранный вами редактор кода и создайте новый файл с именем «db.php». В этом пустом документе вставьте следующие строки кода.

1
2
3
4
5
6
7
8
class Db {
      
    private $mysql;
      
    function __construct() {
        $this->mysql = new mysqli(‘localhost’, ‘root’, ‘yourPassword’, ‘db’) or die(‘problem’);
    }
} // end class

Чтобы создать новый класс, мы используем синтаксис, показанный ниже.

1
2
3
class ‘myClass’ {
  
}

Используя только приведенный выше код, мы успешно создали новый класс. Пока он ничего не делает, но, тем не менее, это класс!

Метод __construct () (класс разговора для «функции») известен как «магический метод». Он будет запущен сразу после создания экземпляра класса. Мы собираемся использовать этот метод, чтобы установить наше первоначальное соединение с базой данных MySql.

1
2
3
function __construct() {
    $this->mysql = new mysqli(‘localhost’, ‘root’, ‘yourPassword’, ‘db’) or die(‘problem’);
}

Если вы не знакомы с ООП, на первый взгляд это может быть немного сложным. К счастью, это не так сложно понять. Мы хотим, чтобы наше соединение mysql было доступно для всех методов нашего класса. Учитывая это, было бы не очень хорошей идеей хранить переменную $ mysql в определенной функции. Вместо этого это должно быть свойство класса.

1
private $mysql;

Внутри метода мы не можем просто получить доступ к нашему свойству, набрав «$ mysql». Сначала мы должны обратиться к объекту.

1
$this->mysql

Обязательно обратите внимание на тот факт, что при доступе к недвижимости мы можем не указывать знак доллара.


При подключении к базе данных предпочтительно использовать улучшенный mysql (mysqli), а не традиционный метод mysql_connect. Это не только быстрее, но и позволяет нам использовать подход ООП.

При создании нового экземпляра класса mysqli мы должны передать четыре параметра.

  • хост : ‘localhost’
  • имя пользователя : root
  • пароль : ваш пароль
  • имя базы данных : дБ

Это должно сделать это сейчас. В течение этого урока мы вернемся к нашему классу, чтобы добавить новые методы. Просто помните, когда мы создаем новый экземпляр этого класса …

1
2
require ‘db.php’;
$db = new Db();

… мы автоматически открываем соединение с нашей базой данных, благодаря магическому методу __construct ().


Теперь нам нужно создать нашу разметку для домашней страницы. Добавьте новую страницу в свое решение и сохраните ее как «index.php». Далее вставьте следующее.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html PUBLIC «-//W3C//DTD XHTML 1.0 Transitional//EN» «http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd»>
<html xmlns=»http://www.w3.org/1999/xhtml»>
<head>
    <meta http-equiv=»Content-Type» content=»text/html; charset=utf-8″ />
    <link rel=»stylesheet» href=»css/default.css» />
    <title>My To-Do List</title>
    <script type=»text/javascript» src=»http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js»></script>
    <script type=»text/javascript» src=»js/scripts.js»></script>
</head>
  
<body>
  
<div id=»container»>
      
<h1>My to-Do List</h1>
  
<ul id=»tabs»>
    <li id=»todo_tab» class=»selected»><a href=»#»>To-Do</a></li>
</ul>
  
<div id=»main»>
      
<div id=»todo»>
</div><!—end todo wrap—>
  
<div id=»addNewEntry»>
</div><!— end add new entry —>
  
</div><!— end main—>
</div><!—end container—>
  
</body>
</html>

В главе нашего документа я ссылаюсь на CDN Google для доступа к jQuery. Это легко предпочтительный метод при использовании jQuery. Далее я ссылаюсь на файл «scripts.js», который мы создадим позже в этом руководстве.

Давайте быстро рассмотрим, для чего предназначен каждый div.

  • контейнер : стандартная упаковка
  • ul # tabs : Наша навигация. Мы будем использовать Javascript для добавления дополнительных вкладок. Я объясню почему в ближайшее время.
  • main : упаковка для основного контента.
  • todo : Tab 1.
  • addNewEntry : вкладка 2


Это не учебник CSS, как таковой. Вы можете свободно просматривать таблицу стилей, которую я использовал. Это в комплекте загрузки. Если вы хотите более глубокий обзор, смотрите скринкаст.


Теперь, когда мы подключились к базе данных и создали нашу разметку / CSS, давайте напишем некоторый код, который будет извлекать строки базы данных.

В разделе «todo» вставьте следующее.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<div id=»todo»>
  
<?php
require ‘db.php’;
$db = new Db();
$query = «SELECT * FROM todo ORDER BY id asc»;
$results = $db->mysql->query($query);
  
if($results->num_rows) {
    while($row = $results->fetch_object()) {
        $title = $row->title;
        $description = $row->description;
        $id = $row->id;
      
echo ‘<div class=»item»>’;
  
$data = <<<EOD
<h4> $Title </h4>
<p> $description </p>
  
<input type=»hidden» name=»id» id=»id» value=»$id» />
  
<div class=»options»>
    <a class=»deleteEntryAnchor» href=»delete.php?id=$id»>D</a>
    <a class=»editEntry» href=»#»>E</a>
</div>
EOD;
          
echo $data;
echo ‘</div>’;
    } // end while
} else {
    echo «<p>There are zero items. Add one now!</p>»;
}
?>
</div><!—end todo wrap—>
  • Используйте ‘require’ для доступа к нашему классу Db.
  • Создайте новый экземпляр класса Db.
  • Создать запрос. Это позволит извлечь все записи из таблицы «todo» и отсортировать их в порядке возрастания.
  • Теперь мы должны выполнить наш запрос. $ Db-> mysql-> запрос ($ запроса). $ db ссылается на объект. $ mysql ссылается на класс mysqli. $ query — это метод класса mysqli, который позволяет нам передавать запрос. Здесь мы передаем строку, которую мы только что создали.
  • $ results-> num_rows вернет общее количество извлеченных строк из базы данных. Если один или несколько возвращаются, мы будем использовать оператор while для циклического перемещения по строкам.
  • Создайте временную переменную с именем $ row, которая будет ссылаться на информацию для каждой итерации. Затем мы создаем три переменные, которые ссылаются на их соответствующие аналоги в базе данных.
  • Каждый элемент будет заключен в div с классом «item».
  • Далее мы используем heredocs для форматирования нашей задачи. Heredocs позволяют легко и организованно смешивать HTML и PHP. Чтобы узнать больше, обязательно просмотрите этот скринкаст .
  • Оберните заголовок в теги h4; описание в тегах p.
  • Пользователю нужен способ редактировать и удалять каждый элемент. Итак, мы создали два тега привязки, которые позволят нам сделать это. Мы вернемся к этому позже.
  • Выведите нашу информацию о heredocs и закройте «.item» div.
  • Если из базы данных было возвращено ноль строк, выведите «Ноль элементов. Добавьте один сейчас!».

Надеюсь, все это имело смысл. На этом этапе у вас должно быть что-то вроде следующего:



Мы также хотим, чтобы у пользователя была возможность вставлять новые записи. Давайте создадим форму, которая позволит эту самую вещь.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<div id=»addNewEntry»>
  
<hr />
<h2>Add New Entry</h2>
<form action=»addItem.php» method=»post»>
    <p>
        <label for=»title»> Title</label>
        <input type=»text» name=»title» id=»title» class=»input»/>
    </p>
  
    <p>
        <label for=»description»> Description</label>
        <textarea name=»description» id=»description» rows=»10″ cols=»35″></textarea>
    </p>
      
    <p>
        <input type=»submit» name=»addEntry» id=»addEntry» value=»Add New Entry» />
    </p>
</form>
  
</div><!— end add new entry —>

Это ваша стандартная «заурядная» форма. Мы добавили материалы для заголовка и описания. При нажатии кнопки отправки введенная информация будет опубликована в «addItem.php». Давайте создадим эту страницу сейчас.


Создайте новый документ и сохраните его как «addItem.php». Вставьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?php
  
require ‘db.php’;
$db = new Db();
  
// adds new item
if(isset($_POST[‘addEntry’])) {
    $query = «INSERT INTO todo VALUES(», ?, ?)»;
      
    if($stmt = $db->mysql->prepare($query)) {
        $stmt->bind_param(‘ss’, $_POST[‘title’], $_POST[‘description’]);
        $stmt->execute();
        header(«location: index.php»);
    } else die($db->mysql->error);
}
  • Обратитесь к нашему классу БД.
  • Создать экземпляр класса.
  • Если кнопка отправки с именем «addEntry» существует, запустите следующий код.
  • Создать новый запрос. Вы заметите, что я использую знаки вопроса в качестве значений. Это предпочтительный способ использовать подготовленные операторы при обновлении нашей базы данных. Это отличный способ защитить себя от инъекций SQL.
  • Подготовьте нашу переменную mysql, передав запрос, который мы только что создали.
  • Если он был подготовлен успешно, свяжите соответствующие параметры. Первый параметр запрашивает типы данных для каждого элемента. Я использовал ‘s’, чтобы обратиться к «строке». Вторые два параметра получают значения заголовка и описания из суперглобального массива POST.
  • Выполните заявление.
  • Наконец, перенаправьте пользователя обратно на домашнюю страницу.


Используя возможности jQuery AJAX, давайте позволим пользователю обновлять каждый элемент без обратной передачи. Создайте новый файл в папке «js» и назовите его «scripts.js». Помните, мы уже ссылались на этот файл в нашей разметке.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
$(function() {
$(‘.editEntry’).click(function() {
    var $this = $(this);
    var oldText = $this.parent().parent().find(‘p’).text();
    var id = $this.parent().parent().find(‘#id’).val();
    $this.parent().parent().find(‘p’).empty().append(‘<textarea class=»newDescription» cols=»33″>’ + oldText + ‘</textarea>’);
    $(‘.newDescription’).blur(function() {
        var newText = $(this).val();
        $.ajax({
            type: ‘POST’,
            url: ‘updateEntry.php’,
            data: ‘description=’ + newText + ‘&id=’ + id,
              
            success: function(results) {
                $this.parent().parent().find(‘p’).empty().append(newText);
            }
        });
    });
    return false;
});
});

Если вы вернетесь к нашей разметке на index.php, вы увидите:

1
2
3
4
<div class=»options»>
    <a class=»deleteEntryAnchor» href=»delete.php?id=$id»>D</a>
    <a class=»editEntry» href=»#»>E</a>
</div>
1
$(‘.editEntry’).click(function() {

Используя jQuery, мы должны отслеживать, когда щелкают тег привязки с классом «editEntry».

1
var $this = $(this);

Далее мы кешируем $ (this) — который ссылается на тег привязки, по которому щелкнули.

1
var oldText = $this.parent().parent().find(‘p’).text();

Нам нужно хранить оригинальное описание. Мы сообщаем тегу привязки, чтобы найти родительский div, и ищем тег p, в котором находится текст описания. Мы получаем это значение с помощью «text ()».

1
var id = $this.parent().parent().find(‘#id’).val();

Чтобы обновить правильную строку в нашей базе данных, мне нужно знать, каков идентификатор этой конкретной строки. Если вы вернетесь к своему коду, вы увидите скрытое поле ввода, которое содержит это значение.

1
<input type=»hidden» name=»id» id=»id» value=»$id» />

Еще раз, мы используем «find» для доступа к этому скрытому вводу, а затем получаем его значение.

1
$this.parent().parent().find(‘p’).empty().append(‘<textarea class=»newDescription» cols=»33″>’ + oldText + ‘</textarea>’);

Теперь нам нужно разрешить пользователю вводить новое описание. Вот почему они нажали «Изменить запись», не так ли? Мы находим тег описания P, очищаем его и затем добавляем текстовую область. Мы используем «empty ()», чтобы убедиться, что мы избавились от всего текста; это больше не нужно. Значение этой текстовой области будет равно oldText — для удобства.


1
$(‘.newDescription’).blur(function() {

Найдите эту новую текстовую область, и когда пользователь покидает текстовое поле, запустите функцию.

1
var newText = $(this).val();

Захватите новый текст, который пользователи вводят в эту текстовую область.

1
2
3
4
5
6
7
8
9
$.ajax({
    type: ‘POST’,
    url: ‘updateEntry.php’,
    data: ‘description=’ + newText + ‘&id=’ + id,
              
    success: function(results) {
        $this.parent().parent().find(‘p’).empty().append(newText);
    }
});

Вызовите функцию .ajax и передайте несколько параметров. Тип будет «POST». URL для доступа является «updateEntry.php». Данные для передачи на эту страницу — это новый текст, введенный пользователем, и уникальный идентификатор из этой строки в базе данных. Когда обновление выполнено успешно, запустите функцию и обновите старый текст новым текстом!

1
return false;

Верните false, чтобы убедиться, что нажатие на тег привязки не направит пользователя в другое место.


Помните, мы назвали нашу PHP-страницу updateEntry с помощью jQuery, но на самом деле мы ее не создали! Давайте сделаем это сейчас. Создайте новую страницу с именем «updateEntry.php» и вставьте следующую.

1
2
3
4
5
6
7
<?php
  
require_once ‘db.php’;
$db = new Db();
$response = $db->update_by_id($_POST[‘id’], $_POST[‘description’]);
  
?>

Как и раньше, мы ссылаемся на наш класс db, а затем создаем его экземпляр. Затем мы создаем новую переменную с именем $ response и делаем ее равной тому, что возвращается методом «update_by_id ()». Мы еще не создали этот метод. Сейчас самое подходящее время для этого.

Вернитесь на свою страницу db.php и добавьте новый метод внизу.

01
02
03
04
05
06
07
08
09
10
11
12
function update_by_id($id, $description) {
    $query = «UPDATE todo
              SET description = ?
          WHERE id = ?
              LIMIT 1″;
            
    if($stmt = $this->mysql->prepare($query)) {
        $stmt->bind_param(‘si’, $description, $id);
        $stmt->execute();
        return ‘good job!
    }
}

Этот метод принимает два параметра: идентификатор и описание элемента. Поэтому, когда мы вызываем этот метод, мы должны помнить, что нужно передать эти два параметра! Мы начнем с создания нашего запроса: обновим таблицу «todo» и изменим описание на то, что было передано, — но обновим только строку, идентификатор которой равен переданному параметру.

Как и в прошлый раз, мы будем использовать подготовленные заявления для обновления нашей базы данных. Это самый безопасный способ! Подготовьте наш запрос, свяжите параметры (string и integer или ‘si’) и выполните. Мы возвращаем универсальную строку, но она действительно не обязательна. Теперь наше обновление должно работать отлично!



Давайте также создадим хороший асинхронный способ удаления записей пользователем. Когда они нажимают кнопку удаления для элемента, мы исчезаем из div и обновляем базу данных, чтобы отразить удаление. Откройте файл javascript и добавьте следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
// Delete anchor tag clicked
$(‘a.deleteEntryAnchor’).click(function() {
    var thisparam = $(this);
    thisparam.parent().parent().find(‘p’).text(‘Please Wait…’);
    $.ajax({
        type: ‘GET’,
        url: thisparam.attr(‘href’),
          
        success: function(results){
            thisparam.parent().parent().fadeOut(‘slow’);
        }
    })
    return false;
});
1
$(‘a.deleteEntryAnchor’).click(function() {

Когда щелкают тег привязки с классом «deleteEntryAnchor», запускается функция.

1
var thisparam = $(this);

Cache $ (this) как thisparam.

1
thisparam.parent().parent().find(‘p’).text(‘Please Wait…’);

Измените текст описания на «Пожалуйста, подождите». Мы должны сделать это, чтобы дать пользователю обратную связь, на случай, если это займет больше времени, чем ожидалось.

1
2
3
4
5
6
7
8
$.ajax({
    type: ‘GET’,
    url: thisparam.attr(‘href’),
              
    success: function(results){
        thisparam.parent().parent().fadeOut(‘slow’);
    }
})

Как и в прошлый раз, мы передаем несколько параметров, которые обращаются к «delete.php». Вместо того, чтобы жестко кодировать страницу в значении URL, я обращаюсь к attr (‘href’) — что равно ‘delete.php? Id = $ id’.

1
<a class=»deleteEntryAnchor» href=»delete.php?id=$id»>D</a>

Нам не нужен параметр «DATA», потому что вся соответствующая информация находится в строке запроса URL. Как только удаление выполнено успешно, мы находим родительский div ‘.item’ и постепенно его уменьшаем.

Мы назвали нашу страницу удаления с помощью jQuery, но мы еще не создали PHP. Создайте новую страницу и добавьте следующий код.

1
2
3
4
5
6
7
<?php
  
require ‘db.php’;
  
$db = new Db();
$response = $db->delete_by_id($_GET[‘id’]);
header(«Location: index.php»);

Вы должны привыкнуть к этим процедурам уже сейчас. Создайте новый экземпляр нашего класса и вызовите метод «delete_by_id». Как только это будет успешно завершено, перенаправьте пользователя обратно в «index.php». Как вы уже догадались, нам нужно создать новый метод в нашем классе db. Вернитесь в db.php и добавьте новую функцию.

1
2
3
4
5
6
function delete_by_id($id) {
    $query = «DELETE from todo WHERE id = $id»;
    $result = $this->mysql->query($query) or die(«there was a problem, man.»);
      
    if($result) return ‘yay!’;
}

Этот метод примет один параметр — идентификатор. Помните: чтобы обновить строку, мы ДОЛЖНЫ знать уникальный идентификатор этой строки. В противном случае он будет обновлять каждую строку. Мы удаляем все строки из таблицы, где идентификатор равен тому, что передано. Поскольку каждая строка имеет свой уникальный идентификатор, это повлияет только на одну. Далее мы передаем этот запрос нашему объекту mysql. Еще раз, возвращение не является необходимым; это просто для удовольствия.


Мы завершили всю нашу работу с PHP! Последний шаг — добавить немного jQuery, чтобы все работало лучше. В верхней части файла Javascript, сразу после метода document.ready, добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
// Don’t display the addNewEntry tab when the page loads.
$(‘#addNewEntry’).css(‘display’, ‘none’);
  
// We’re using jQuery to create our tabs.
// this, we should append our tabs, so that they won’t show up if disabled.
$(‘#tabs’).append(‘<li id=»newitem_tab»><a href=»#»>New Item</a></li>’);
  
// Hide the description for each to-do item.
$(‘div.item’).children().not(‘h4’).hide();
  
// The entire item div is clickable.
// When this div is clicked, we’re going to toggle the display from visible to hidden each time it’s clicked.
// However, when the user clicks the «update» button, the div will close when they click inside the textarea
// to edit their description.
// we do nothing.
$(‘div.item’).css(‘cursor’, ‘pointer’).click(function(e) {
    if (!$(e.target).is(‘textarea’)) {
        $(this).children().not(‘h4’).slideToggle();
        $(this).children(‘h4’).toggleClass(‘expandDown’);
    }
});

Я прокомментировал каждый шаг довольно хорошо. Поэтому я воздержусь от повторения. Ваш окончательный файл scripts.js должен выглядеть следующим образом.

1
$(function() { // Don’t display the addNewEntry tab when the page loads. $(‘#addNewEntry’).css(‘display’, ‘none’); // We’re using jQuery to create our tabs. If Javascript is disabled, they won’t work. Considering // this, we should append our tabs, so that they won’t show up if disabled. $(‘#tabs’).append(‘<li id=»newitem_tab»><a href=»#»>New Item</a></li>’); // Hide the description for each to-do item. Only display the h4 tag for each one. $(‘div.item’).children().not(‘h4’).hide(); // The entire item div is clickable. To provide that feedback, we’re changing the cursor of the mouse. // When this div is clicked, we’re going to toggle the display from visible to hidden each time it’s clicked. // However, when the user clicks the «update» button, the div will close when they click inside the textarea // to edit their description. This code detects if the target of the click was the textarea. If it was, // we do nothing. $(‘div.item’).css(‘cursor’, ‘pointer’).click(function(e) { if (!$(e.

Мы пока не можем назвать это днем! Этот забавный Internet Explorer 6 вызывает несколько проблем с макетом.


  1. Фоновые pngs являются 24-битными. IE6 изначально не поддерживает это. Нам нужно импортировать скрипт, чтобы исправить это.
  2. Вкладки навигации не отображаются в нужном месте.
  3. Каждый элемент div.item отображается неправильно при развертывании.
  4. Наши кнопки редактирования и удаления расположены слишком далеко справа от нашего div.

Хотя нам, возможно, понравится, мы пока не можем игнорировать этот браузер. К счастью, вы обнаружите, что большинство проблем IE6 можно исправить довольно легко. Во-первых, нам нужно импортировать скрипт, который исправит проблему альфа-прозрачности. У Дина Мартина есть фантастический файл Javascript, который приводит IE6 в соответствие со стандартами. Просто добавив «-trans» в конец 24-битных имен файлов png, мы сможем решить нашу проблему. Обязательно посетите папку с изображениями и отредактируйте имена.

1
2
3
4
<!—[if lt IE 7]>
    <script src=»http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE7.js» type=»text/javascript»></script>
    <link rel=»stylesheet» href=»css/ie.css» />
<![endif]—>

CDN от Google снова приходит на помощь, предоставляя размещенную версию сценария IE7. Это решает нашу проблему прозрачности, но у нас все еще есть несколько причуд.


Обратите внимание, что в нашем условном выражении мы также импортировали файл ie.css. Создайте этот файл прямо сейчас и вставьте следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
body {
margin: 0;
}
  
#tabs {
height: 100%;
  
}
  
#main {
height: 100%;
}
  
#main div.item {
width: 100%;
overflow: hidden;
position: relative;
}

Вы обнаружите, что добавление «position :lative», «overflow: hidden» и «height: 100%» решит 90% ваших проблем с IE6. Теперь наш макет отлично работает во всех браузерах!



Там было много, чтобы покрыть здесь. Надеюсь, я объяснил себе достаточно подробно. Если нет, то для этого и есть соответствующая скринкаст! Обязательно просмотрите его, чтобы очистить все размытые области. Если у вас остались вопросы, просто спросите меня! Спасибо за прочтение.