Гуру jQuery, можно ли упростить этот код?

#jquery #html #css

#jquery #HTML #css

Вопрос:

Живая демонстрация: http://jsfiddle.net/stapiagutierrez/KKdsb/29 /


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

Поскольку я новичок в jQuery, возможно, я делаю все окольным путем.

Есть предложения по улучшению?

 ul {
    list-style-type:none;
    margin-top:10px;
    margin-left:10px;
    border:1px solid #333;
    border-radius:8px;
    overflow:hidden;
    width:111px;
    cursor:pointer;
}

li {
    float:left;
    margin-left:5px;
    padding-top:2px;
}

<ul>
    <li>
        <img class="onestar rating" src="http://i.imgur.com/8LA1i.png" alt="no-star" />
    </li>

    <li>
        <img class="twostar rating" src="http://i.imgur.com/8LA1i.png" alt="no-star" />
    </li>

    <li>
        <img class="threestar rating" src="http://i.imgur.com/8LA1i.png" alt="no-star" />
    </li>

    <li>
        <img class="fourstar rating" src="http://i.imgur.com/8LA1i.png" alt="no-star" />
    </li>

    <li>
        <img class="fivestar rating" src="http://i.imgur.com/8LA1i.png" alt="no-star" />
    </li>
</ul>

$(document).ready(function() {
    $(".rating").click(function() {
        if ($(this).hasClass("onestar")) {
            alert("Clicked on 1 star!");
        }
        else if ($(this).hasClass("twostar")) {
            alert("Clicked on 2 stars!");
        }
        else if ($(this).hasClass("threestar")) {
            alert("Clicked on 3 stars!");
        }
        else if ($(this).hasClass("fourstar")) {
            alert("Clicked on 4 stars!");
        }
        else if ($(this).hasClass("fivestar")) {
            alert("Clicked on 5 stars!");
        }
    });

    $(".rating").mouseleave(function() {
        $(".rating").each(function() {
            $(this).attr("src", "http://i.imgur.com/8LA1i.png");
        });
    });

    $(".rating").mouseover(function() {
        if ($(this).hasClass("onestar")) {
            $(".onestar").attr('src', 'http://i.imgur.com/X68bI.png');
        }
        else if ($(this).hasClass("twostar")) {
            $(".onestar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".twostar").attr('src', 'http://i.imgur.com/X68bI.png');
        }
        else if ($(this).hasClass("threestar")) {
            $(".onestar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".twostar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".threestar").attr('src', 'http://i.imgur.com/X68bI.png');
        }
        else if ($(this).hasClass("fourstar")) {
            $(".onestar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".twostar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".threestar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".fourstar").attr('src', 'http://i.imgur.com/X68bI.png');
        }
        else if ($(this).hasClass("fivestar")) {
            $(".onestar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".twostar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".threestar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".fourstar").attr('src', 'http://i.imgur.com/X68bI.png');
            $(".fivestar").attr('src', 'http://i.imgur.com/X68bI.png');
        }
    });
});
 

Комментарии:

1. Пожалуйста, также включите сюда код jquery — если jsfiddle выйдет из строя, этот вопрос не может много значить для кого-либо еще.

Ответ №1:

Это было довольно забавно, вот jsFiddle: http://jsfiddle.net/KKdsb/30 /

ПРИМЕЧАНИЕ: использование двух parent() функций в mouseover обработчике зависит от вашей структуры html. Я бы рекомендовал класс или аналогичный для прямого доступа к родительскому <ul> элементу $(this).closest('.classname') .

Код jQuery:

 $(document).ready(function() {
    $(".rating").click(function() {
        var position = $(this).parent().index();
    });

    $(".rating").mouseleave(function() {
        $(".rating").each(function() {
            $(this).attr("src", "http://i.imgur.com/8LA1i.png");
        });
    });

    $(".rating").mouseover(function() {
        var position = $(this).parent().index()   1;
        $(this).parent().parent().find("li:lt(" position ") > img").each(function (i, e) {
            $(e).attr('src', 'http://i.imgur.com/X68bI.png');
        });
    });
});
 

Ссылка:

Комментарии:

1. Привет, Андре — я отредактировал ваш ответ, чтобы поменять .parent() на .parents() , но я думаю .closest() — это то, что вы хотите. .closest() остановится, когда достигнет первого совпадения, где .parents() продолжит движение вверх по дереву DOM в поисках дальнейших совпадений.

2. Привет, Beejamin, я отредактировал свой ответ, чтобы включить closest() , спасибо за предложение!

Ответ №2:

Это отличный кандидат для использования спрайтов изображений. По сути, вы помещаете все свои звездные рейтинги в один файл изображения и перемещаете этот файл с помощью CSS ‘background-position’. Основное преимущество этого способа заключается в том, что вы делаете только один запрос для изображения звезды — вы не меняете путь к начальному изображению повторно (что может сделать работу немного неудобной и медленной, в зависимости от браузера.

Вот пример использования спрайтов изображений: http://www.spookandpuff.com/examples/starRatings/starRatings.html

Вот спрайт изображения, который я использовал:

Спрайт с изображением 5 звезд

А вот HTML, CSS и Javascript:

HTML очень прост:

 <div class="starRating">
    <ol class="ratingValues">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
    </ol>
  </div>
 

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

CSS также довольно прост:

       .starRating {
        width:105px;
        height:20px;
        float:left;
        margin:20px 0;
      }

      .ratingValues {
        float:left;
        width:100%;
        list-style:none;
        background:url(starSprite.png) no-repeat 0 0;
      }

      .ratingValues li{
        display:block;
        float:left;
        width:21px;
        height:20px;
        text-indent:-999em;
        overflow:hidden;
        cursor:pointer;
      }

      .rating_1 .ratingValues {background-position:0 -20px}
      .rating_2 .ratingValues {background-position:0 -40px}
      .rating_3 .ratingValues {background-position:0 -60px}
      .rating_4 .ratingValues {background-position:0 -80px}
      .rating_5 .ratingValues {background-position:0 -100px}
 

Здесь мы настраиваем поле таким же размером, как один ряд звездочек из спрайта. Элементы списка становятся квадратами нужного размера (по одному квадрату на «звезду» на графике). Я использовал отрицательный текстовый отступ, чтобы скрыть текст внутри каждого <li> .

Наиболее интересным является последний блок. Здесь мы настраиваем положение фона спрайта в зависимости от класса содержащего элемента, поэтому, когда .ratingValues находится внутри чего-либо с классом ‘rating_3’, фон переместится в положение: 0 -60px.

Бьюсь об заклад, вы видите, к чему это ведет. Вот javascript:

 $(function(){
  starRatings = $('.starRating'); //Cache this to make future references faster

  starRatings.each(function(){ //Iterate through each rating, in case there's more than one per page
    var thisRating = $(this),
        triggers = $('.ratingValues > li', this); //More caching

    triggers 
      .hover(
        function(){ //The hover-over event
          var trigger = this;
          thisRating.addClass(function(){
            return 'rating_'   (triggers.index(trigger)   1); //This sets the class to rating_   the number of the element (1 - 5)
          })
        },
        function(){  //The hover-out event, just clears the classes
          thisRating.removeClass('rating_1 rating_2 rating_3 rating_4 rating_5');
        })
        .click(function(){ //The click event
          alert('Clicked '   (triggers.index(this)   1));
        });
  });

});
 

Это находит любой элемент с классом of .starRating и назначает обработчики событий для <li> s внутри него. Наведение курсора мыши на <li> s присваивает класс div-элементу ‘StarRating’, который в основном сообщает нам, какое значение выбрано (от rating_1 до rating_5). На этом этапе запускается CSS и перемещает фоновый спрайт в нужное положение.

Для дополнительных очков вы можете поставить стандартный якорь, что-то вроде этого: <a href="{rateThis?rating=1}">1</a> внутри каждого LI , который отправит запрос на ваш сервер и установит рейтинг по прямому HTTP (без javascript). Таким образом, все по-прежнему работает, если Javascript отключен.

Надеюсь, это достаточно ясно — кричите, если вам нужна дополнительная помощь!

Комментарии:

1. Мне кажется, что ОП просит упростить его код. Вы увеличили сложность, но в итоге получили такой же объем общего кода. Является ли использование спрайтов изображений хорошей идеей? Да! Но ваше решение вряд ли лучше оригинального.

2. ОП сказал, что он новичок в jQuery, поэтому я привел пример с большим количеством объяснений и безопасности в виде кэширования, чтобы это решение было надежным и пригодным для повторного использования. В нем показаны некоторые полезные приемы для начинающих в работе с DOM, обработке событий jQuery и взаимодействии CSS / JS. Я поддерживаю это. Уменьшение сложности зависит не только от объема кода, но и от того, насколько он понятен.

3. Не совсем ответ на вопрос, но я думаю, что это отличный вариант. Мне на самом деле очень нравится, как звезды заполняют спрайты, JS выглядит здесь немного медленно. Я бы полностью пошел на это в будущих проектах 🙂

4. Другая, более реальная проблема: как вы собираетесь отображать частичные оценки? Т.е. Оценка 4,5 amazon.com/Greatest-Hits-2/product-reviews/B005SO6HT6 /…

5. Изображение спрайта можно легко расширить, чтобы покрыть половину или четверть точек. Это не было бы хорошим решением, если бы вам нужно было показать большую точность, чем это.

Ответ №3:

Улучшенное решение

Пример здесь

HTML:

 <div>
    <div class="rating"><div class="value"></div></div>
</div>
 

CSS:

 .rating {
    width:105px;
    height:20px; 
    background: url(http://i.stack.imgur.com/oZ9Id.png) no-repeat;
    cursor:pointer;
}
.rating .value {
    width:0;
    height:20px;   
    background: url(http://i.stack.imgur.com/oZ9Id.png) no-repeat 0px -100px;  
}
 

Javascript

 $(document).ready(function() {
    var starWidth = $(".rating").width() / 5;
    $(".rating").click(function(e) {
        $(this).find(".value").css({width:(Math.ceil(e.offsetX / starWidth) * starWidth) "px"});
    });
});
 

Оригинальное решение: здесь


Плюсы этого решения по сравнению с другими в этой теме:

  1. Минимальное количество кода, разметки и CSS. Очень легко читать и поддерживать.
  2. Решает общую проблему дизайна в операционной системе, а не пытается исправить ее с помощью jQuery sugar.
  3. Поддерживает оценки с плавающей запятой (просто установите ширину на значение / MaxWidth)
  4. Поддерживает переключение между интерактивными и доступными только для чтения представлениями путем простого отключения события щелчка (нет необходимости изменять HTML).
  5. После 3 и 4 — это единый элемент управления для отображения как индивидуальных пользовательских оценок, так и составных оценок.
  6. Меньший размер изображения, чем требуется для ответа Beejamin (он использует только первую и последнюю строки).
  7. Никаких сложных манипуляций с DOM или поисков в CSS

Минусы: я ничего не вижу.

Минусы исправлены с момента последней итерации:

  1. Удалена зависимость js от ширины изображения. Теперь он вычисляется динамически, хотя я все еще думаю, что в этом нет необходимости.

Полная и полностью функциональная версия здесь. Необычная версия с анимацией щелчка здесь

Комментарии:

1. Тогда как вы определяете, сколько звезд показывать? Вы проверяете положение X, в котором мышь находится в элементе? Это звучит сложнее без причины.

2. Разве это не было бы лучшим комментарием, чем ответом?

3. @SergioTapia почему это сложно?

4. @Flimzy у него нет проблемы с jQuery. У него есть общая проблема с дизайном, которую он пытается решить с помощью jQuery, что довольно бессмысленное занятие.

5. Ну, вы должны рассчитать ширину каждой отдельной звезды и убедиться, что она центрирована.