• В чем заключается минус такого делегирования?

    rockon404
    @rockon404
    Frontend Developer
    И что дальше? Проверка if (target.tagName != 'TD') return; справляется со своей задчей - игнорирует все, кроме td.

    Так вы кликните по td, а попадете на вложенный элемент strong, вызов прервется и td не подсветится, так как
    target == 'strong'
    хотя вы кликнули по td.

    Затем и нужна беготня по иерархии. Примерно так же делегирование jQuery работает:
    $('table').on('click', 'td', function() { 
      //handler 
    });

    Там такая же "беготня" в реализации.
    Ответ написан
    1 комментарий
  • Возвращаем функции как результаты?

    rockon404
    @rockon404
    Frontend Developer
    Обращение идет к первому аргументу.
    Вы при вызове:
    var snakify = attitude(/millenials/ig, "Snake People");

    возвращаете в snakify такую функцию:
    function(source) {
      return source.replace(/millenials/ig, "Snake People");
    };


    и она принимает только один аргумент source.
    Ответ написан
    1 комментарий
  • Почему в href всегда 2009?

    rockon404
    @rockon404
    Frontend Developer
    Потому что каждую итерацию перезаписываете всем элементам с тегом a атрибут href и все элементы получают одинаковое значение.
    Надо так:
    var date = new Date();
    var year = date.getFullYear();
    var listGroup = $('.list-group');
    
    for(var i = 0; i < 10; i++) {
      var currentYear = year - i;
      var a = $('<a href="' + currentYear + '" class="list-group-item">' + currentYear + '</a>');
      listGroup.append(a);
    }
    Ответ написан
    Комментировать
  • Использование Api сайтом, нужно ли?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Почему нельзя, просто, выделить common часть для обоих модулей?
    А в модель поста можно прописать метод который будет доставать и мапить результаты.

    Тогда у вас будет примерно такой код в rest:
    router.get('/', routeCache.cacheSeconds(20), async (req, res) => {
        const posts = await models.Post.getPostsList({ limit: 10 });
    
        res.json(posts);
    });


    и такой для сайта:
    router.get('/', async (req, res) => {
        const posts = await models.Post.getPostsList({ limit: 10 });
    
        res.render('index', { posts_list: posts });
    });


    Все просто и никаких костылей.
    Обращения сервера сайта к rest это лишний код и лишние операции.

    Метод res.json() сам добавляет к ответу заголовок Content-Type со значением 'application/json' если он не добавлен до этого.
    Ответ написан
    Комментировать
  • Приведенный мною код - одно и то же? Или один работет быстрее / эффективнее, а другой - нет?

    rockon404
    @rockon404
    Frontend Developer
    Да нет особой разницы. Накидал бенчмарк для трех вариантов делегирования:

    p0:
    $('.wrapper').on('click', '.btn', function() { 
      //handler 
    });


    p1:
    $('.wrapper').on('click', function(e) {
      if ($(e.target).hasClass('btn')) {
        //handler 
      }
    });


    p2:
    document.querySelector('.wrapper').addEventListener('click', function(e) {
      if(e.target && e.target.classList.contains('btn')) {
         //handler 
      }
    });


    Результат для каждого это доли от одной тысячной секунды.

    Смело используйте самый удобный для конкретного случая вариант.

    Вариант с условием p1 подойдет для объединения слушателей нескольких элементов(например все кликабельные элементы карточки товара). Так быстрей пройдет инициализация и потребуется меньше памяти. Другое дело, что операции сравнения тоже не дешевые и один GodEventHandler тоже делать не стоит.

    Так же если у кнопки есть дочерние элементы, то тут лучше использовать вариант p0 , так как он определяет целевой элемент как e.curentTarget, дочерний элемент клика как e.target и делегата на котором будет слушать событие как e.delegateTarget и сработает точно про клику по целевому элементу или его детям. В случаях p1 и p2 клик по дочернему элементу пройдет мимо условий, так как e.target будет дочерним элементом, а не целевым.

    Вот, что точно делать не надо, так это вешать без надобности слушатели на document. Делегировать события надо, по возможности, как можно ближе к целевому элементу. Если вы добавляете кнопки динамически в определенный контейнер, то вешайте обработчик события на него, так слушатель будет срабатывать только по клику внутри контейнера. В вашем же случае он будет срабатывать при каждом клике на любом элементе страницы, создавая тем самым дополнительную нагрузку на браузер.
    Ответ написан
    4 комментария
  • Как решить проблему с row/colspan?

    rockon404
    @rockon404
    Frontend Developer
    Посмотрите как это реализовано в правильных инструментах.
    5a515aeaa4db0356957060.png
    1. В таблице должна быть возможность выделить ячейки.
    2. По клику на кнопку объединить, удаляете выделенные, заменяя одной большой. Можно даже контент сохранить.
    А так как вы хотите реализовать, во-первых неудобно пользователю, во-вторых усложняет реализацию.
    Ответ написан
    Комментировать
  • Почему реакт достает токен из куков лишь однажды?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Ваши пояснения больше путают, чем помогают. Вопрос не понял, но думаю дело в том, что токен достаете при инициализации. А надо дергать при каждом вызове makeParams:
    const makeParams = params => ({
      headers: {
        Authorization: `Bearer ${cookies.get('frl_jwt_token')}`,
      },
      params: { ...params },
    })
    Ответ написан
    Комментировать
  • Правильно ли я понимаю что такое API?

    rockon404
    @rockon404
    Frontend Developer
    Сама аббревиатура API расшифровывается как программный интерфейс приложения, интерфейс на уровне чего угодно от класса до целого сервиса. Вы наверное имели ввиду API веб-сервиса или REST API.
    Как бы объяснить попонятней, API веб-сервиса это публичный или закрытый интерфейс взаимодействия, реализуемый посредством HTTP-запросов (GET, POST, PUT, PATCH, DELETE) и ответов. Ответы обычно в формате JSON.
    Обработчики конкретных запросов на сервере называют эндпоинтами. Пример типичных эндпоинтов:
    GET 'api/v1/products' - вернет массив товаров.
    GET 'api/v1/product/:id' - вернет один товар по id
    POST 'api/v1/product' - добавляем товар, отправив в теле запроса JSON с данными и получаем в ответ его же, но уже с присвоенным базой данных id или просто id.
    PATCH 'api/v1/product/:id' - обновляем товар отправляя измененные поля в виде JSON и получаем измененный товар или success.
    DELETE 'api/v1/product/:id' - удаляем товар, получая в ответе success.

    Основные пользователи REST API это мобильные и веб приложения. Часто многие сервера и сайты используют API сторонних сервисов.
    Ответ написан
    Комментировать
  • Как назвать папку с JS, CSS, Images, Fonts?

    rockon404
    @rockon404
    Frontend Developer
    └── assets
          ├── css/
          ├── js/  
          ├── fonts/
          └── images/
    Ответ написан
    Комментировать
  • Как восстановить проект Visual Studio после форматирования диска?

    rockon404
    @rockon404
    Frontend Developer
    1. Зря устанавливали Windows. Надо было бежать к любому знакомому восстанавливать данные. Восстановили бы весь проект.
    2. В будущем используйте git и удаленные репозитории github, bitbucket, gitlab etc.
    3. Скорей всего проект полностью уже не восстановить. Пишите по-новой используя уцелевшие файлы.

    sticker,375x360-bg,ffffff.u1.png
    Ответ написан
    6 комментариев
  • Программисту нужна слепая печать?

    rockon404
    @rockon404
    Frontend Developer
    Я считаю, что это один из обязательных навыков, которые должен освоить каждый программист.
    Работать в разы комфортней и быстрей.
    Скорости в 120-180 символов в минуту хватит с головой.
    Ответ написан
    Комментировать
  • Как запретить перенос на новую строку в textarea?

    rockon404
    @rockon404
    Frontend Developer
    Используйте вместо textarea:
    <div contenteditable="true"></div>

    Обработка события:
    function (event) {
      if (!event.shiftKey && event.which === 13) {
        event.preventDefault();
        // тут код для отправки сообщения
      }
    }


    Пример на Vue:

    <div id="app">
       <div class="wrapper">
          <div class="text-box" contenteditable="true" v-on:keypress="handleKeypress($event)"></div>
       </div>
    </div>


    new Vue({
      el: '#app',
      methods: {
        handleKeypress: function(e) {
          if (!e.shiftKey && e.which === 13) {
            e.preventDefault();
           }
        }
      },
    })
    Ответ написан
    9 комментариев
  • Почему этот код работает только в опере?

    rockon404
    @rockon404
    Frontend Developer
    В верхнем скрипте у вас не вызывается функция. Не знаю что у вас в Opera работает, но точно не верхний скрипт.
    Исправить можно так:
    'use strict';
    var _POS = {};
    (function () {
      navigator.geolocation.getCurrentPosition(function (position) {
           var lat = position.coords.latitude;
           var lng = position.coords.longitude;
    
           _POS.lat = lat;
           _POS.lng = lng;
        });
    }()); // <== тут добавлен вызов функции


    Данные по позиции получаются асинхронно, это надо учитывать. То есть если у вас код использует данные не дождавшись их получения, его надо переписать.

    (function () {
      navigator.geolocation.getCurrentPosition(function (position) {
          /* 
           *    инициализировать модули и функции использующие
           *    данные подученные от навигатора надо тут
           *    
           */      
        });
    }());
    Ответ написан
  • Объясните, пожалуйста, строку кода?

    rockon404
    @rockon404
    Frontend Developer
    Самовызывающаяся функция.
    Все варианты записи:
    strict = (function() {return !this;}());
    
    strict = (function() {return !this;})();
    
    strict = function() {return !this;}();


    Пара скобок в конце это вызов этой функции. Можно переписать так:
    function func() {
      return !this;
    }
    
    var strict = func();
    Ответ написан
    3 комментария
  • Сортировка массива?

    rockon404
    @rockon404
    Frontend Developer
    Вот универсальный и расширяемый вариант:
    var auto = [
      {
        marka: "BMW",
        model: "4k",
        cuzov: "A",
     },
     {
       marka: "Merc",
       model: "4k",
       cuzov: "B",
     },
     {
       marka: "Audi",
       model: "3k",
       cuzov: "C",
     },
     {
        marka: "Toyota",
        model: "1k",
        cuzov: "D",
     },
     {
        marka: "Lexus",
        model: "2k",
        cuzov: "E",
     },
     {
        marka: "Rang",
        model: "3k",
        cuzov: "F",
     },
     {
       marka: "BMW",
       model: "4k",
       cuzov: "G",
     },
     {
       marka: "BMW",
       model: "2k",
       cuzov: "Y",
     },
     {
       marka: "Audi",
       model: "3k",
       cuzov: "U",
     }
    ];
    
    var marka = ["Audi", "Rang", "Merc"];
    var model = ["4k", "2k", "3k"];
    var cuzov = [];
    var params = { marka, model, cuzov };
    var activeKeys = Object.keys(params).filter(key => params[key].length);   
      
    var rez1 = auto.filter(x => activeKeys.every(key => params[key].includes(x[key])) );
    
    console.log(rez1);


    Можно расширять объект параметров фильтрации. Параметры без элементов, отфильтровываются до этапа сравнения.
    Ответ написан
    2 комментария
  • Как правильно получать обновленный Store в React Reduxe?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    В render такие действия выполнять нельзя. Хоть dispatch и выполняется синхронно, свойства получаемые компонентом иммутабельны, а это значит, что компонент получит новые свойства, выполнит все функции жизненного цикла и получит новый массив men, только в следующий render.

    Для добавления вашей сущности можно использовать хандлер:
    class ActionsReducers extends React.Component {
      handleAddMan = () => {
        this.props.addMan({ name: 'Альберт', family: 'Эйнштейн' });
      };
      
      render() {
        const { men } = this.props;
    
        return (
          <Wrapper>
            <List>
              {men.map(man => <Man key={man.id} man={man} />)}
              {!men.length && <Empty>No data in list.</Empty>}
            </List>
            <Button onClick={this.handleAddMan}>Add man</Button>
          </Wrapper>
        );
      }
    }


    Или один из методов жизненного цикла:
    class ActionsReducers extends React.Component {
      componentDidMount() {
        this.props.addMan({ name: 'Альберт', family: 'Эйнштейн' });
      }
    
      render() {
        const { men } = this.props;
    
        return (
          <Wrapper>
            <List>
              {men.map(man => <Man key={man.id} man={man} />)}
              {!men.length && <Empty>No data in list.</Empty>}
            </List>
          </Wrapper>
        );
      }
    }
    Ответ написан
  • Как в React Reduxe получить Store переданный через Provider?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Макс Мачез, вам не надо обращаться к store напрямую в компоненте, если вы используете react-redux.
    Вы не должны мапить весь стор, как это делаете в примере в комментарии, мапьте только данные необходимые компоненту:
    const mapStateToProps = state => ({
      userData: state.userData,
    });


    по-хорошему, для получения части объекта состояния, надо использовать selector pattern, отличная его реализация reselect:
    import { createSelector } from 'reselect';
    
    export const userDataSeleror = state => state.userData;
    
    export const userSelector = state => createSelector(
      userDataSelector,
      userData => userData.user,
    );
    
    export const isUserDataLoadingSelector = state => createSelector(
      userDataSelector,
      userData => userData.isLoading,
    );
    
    export const isUserDataErrorSelector = state => createSelector(
      userDataSelector,
      userData => userData.isError,
    );


    тогда контейнер для компонента будет примерно такой:
    import { fetchUserData } from './somePlace';
    import Profile from './Profile';
    import {
      userSelector,
      isUserDataLoadingSelector,
      isUserDataErrorSelector,
    } from './selectors';
    
    const mapStateToProps = state => ({
      user: userSelector(state),
      isLoading: isUserDataLoadingSelector(state),
      isError:  isUserDataErrorSelector(state),
    });
    
    const mapDispatchToProps = {
      fetchUserData,
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Profile);


    Компонент внутри вообще ничего не должен знать про redux, он лишь получает набор обязательных свойств и использует, а откуда они приходят ему не важно:
    class Profile extends Component {
      componentDidMount() {
        this.props.fetchUserData();
      }
      
      render() {
        const { user, isLoading, isError } = this.props;
        return (
           ...
        );
      }
    }


    Все, что вы передаете посредством декоратора connect в компонент, доступно в последнем в его свойствах (this.props), аргументы поэтому так и называются mapStateToProps и mapDispatchToProps. Все actionCreators переданные объектом в mapDispatchToProps оборачиваются в dispatch, внутренним вызовом bindActionCreators.
    Ответ написан
    2 комментария