8 crímenes en jQuery que nunca deberías cometer

Cualquiera puede programar en jQuery o JavaScript, ¿hay alguna duda de ello? Ahora, lo realmente interesante está en programar teniendo en cuenta la optimización del código, teniendo en cuenta todos los entresijos del lenguaje para poder entregar la aplicación con el mejor rendimiento posible. Y como yo nos los conozco todos, pero sí hay gente que lo hace,  sale este artículo.

Crímen #1, no usar la versión de jQuery alojada en Google

De lógica, casi, si sale una versión nueva de jQuery (y casi de cualquier framework), por algo será. En el caso de jQuery las nuevas versiones siempre vienen cargadas de correcciones de bugs y mejoras de rendimiento brutales. Si somos un poco manitas, tendremos muchos proyectos pululando por la web, ¿vas a ir uno por uno resubiendo y retocando todos para cambiar la versión de jQuery? Lo dudo. Por ello, la solución es usar el que está alojado en la CDN de Google, que no suele fallar. Y si falla, no os preocupéis, seguid leyendo.

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>

Crímen #2, no tener una chuleta a mano

Y no sólo para los no-iniciados, puede que te acostumbres a trabajar de una forma que no es la más indicada y la memoria no es infinita. Ejemplos de buenas hojas de chuletas:

Crímen #3, no tener un fallback a local cuando falle la CDN

Relacionado al crímen #1. Si Google cae, por alguna extraña razón o el fin del mundo, que no te pille desprevenido. Es un script mega-sencillo que chequea si la ventana tiene jQuery activo, si no, crea el elemento script con tu versión de jQuery en local:

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script type="text/javascript">window.jQuery || document.write('<script src="js/jquery-1.8.1.min.js"></script>')</script>

Útil también para cuando trabajes en local y se te pierda la conexión, mucho más probable que lo de Google.

Crímen #4, usar selectores repetitivamente

Creía que esto era algo más que obvio para todos los programadores, pero parece que hay una oleada de código malo en jQuery pululando por la web. Cuando usas un selector, jQuery tiene que encontrar el elemento al que haces referencia, con su consecuente gasto de tiempo. Si tienes que usar el mismo elemento para modifcar varias propiedades del mismo, ¿no es mejor encadenarlas?

La versión cutre:

$("#mySmashingID").css("color","pink"); $("#mySmashingID").css("font","Verdana"); $("#mySmashingID").text("Some error message goes here!");

Versión correcta:

$("#mySmashingID").css({ "color": "pink", "font": "Verdana"}).text("Some error message goes here!!");

Crímen #5, no cachear los elementos apropiadamente

Uno de los más importantes en cuanto a rendimiento, parecido al anterior. Si tienes que usar un elemento varias veces, guárdalo en una variable, no recorras el DOM cada vez para encontrarlo.

Versión cutre:

 $(‘#mySmashingGag’).appendTo(‘#mysidebar’);
 $(‘#mySmashingGag’).addClass(‘widget’);
 $(‘#mySmashingGag’).hide();
 $(‘#mySmashingGag’).fadeIn(‘fast’);

Versión correcta:

 var mySmashingGag = $(‘#mySmashingGag’);
 mySmashingGag.appendTo(‘#mysidebar’);
 mySmashingGag.addClass(‘widget’);
 mySmashingGag.hide();
 mySmashingGag.fadeIn(‘fast’);

Crímen #6, no usar los IDs cuando puedes hacerlo

Entrando en el funcionamiento de jQuery, cuando usas un selector con una clase, jQuery recorre todo el árbol DOM para encontrar los elementos; en cambio, si usas un selector con el ID del elemento, usa la función nativa findElementById() que, como imaginarás, es mucho más rápida. Otra versión de lo mismo es la modificación de la selección por IDs para su optimización:


$("#hola").find("div.barra");

En lugar de


$("#hola div.barra");

Crímen #7, otra forma de escribir el $(document).ready

Todos sabréis para qué sirve el $(document).ready (ejecutar funciones tan pronto esté cargado el documento, en grosso), otra forma de escribirlo es con una función estilo lambda:


$(function(){

//cosas guay-ses

});

Crímen #8, no dar contexto

Simple: tratar de filtrar el contexto donde jQuery tiene que buscar.


$(".miClase","#MiElemento").on("click",hacerCosasMolonas);


Cómo incluir la política de privacidad en opciones de configuración de la aplicación en Windows 8

Hace unos días estaba subiendo una de mis primeras aplicaciones para la Windows 8 Store, así que, antes de subirla, decidí darle un último repaso a las exigencias de la Store, entonces recordé algo que vi en el MSP Summit de Madrid de la semana pasada: si usas Internet en tu app, debes incluir una política de privacidad dentro del menú de configuración de tu aplicación, enmarcada dentro de la barra de charms. Tras una búsqueda rápida por los foros de MSDN no encontré nada, así que, yendo a algo más potente, estos son los resultados de la búsqueda. Vamos allá:

Primero hemos de crear una web donde dejemos clara nuestra política de privacidad. En mi caso, la aplicación no recogía datos de usuario de ningún tipo, sencillamente usaba Internet para mostrar información, así que tomé uno cualquiera de ejemplo y lo modifiqué, quedando así. Guarda el link a tu página, ya que tendrás que ponerlo cuando subas la aplicación a la store.

Segundo vamos al código. La mecánica es sencilla, nos suscribimos al evento CommandsRequested de la vista actual en otro evento que tengamos o método. Lo suyo es hacerlo en el OnNavigatedTo o LoadState, aunque me gusta separarlos y usarlos acorde a su nombre. Así que, al OnNavigatedTo:


protected override void OnNavigatedTo(NavigationEventArgs e){

SettingsPane.GetForCurrentView().CommandsRequested += principal_CommandsRequested;

}

Después de esto el paso es obvio, crear el principal_CommandsRequested :


void principal_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args){

AddSettingsCommands(args);

}

¿Alguien duda del siguiente paso?


public static void AddSettingsCommands(SettingsPaneCommandsRequestedEventArgs args)

{

args.Request.ApplicationCommands.Clear();

SettingsCommand privacyPref = new SettingsCommand("privacyPref", "Política de Privacidad", (uiCommand) =>{Windows.System.Launcher.LaunchUriAsync(newUri("http://danielrozo.es/privacy-policy")); });

args.Request.ApplicationCommands.Add(privacyPref);

}

Y con esto debería estar todo hecho, compiláis y corréis la aplicación y veréis cómo sale “Política de Privacidad” bajo la configuración de vuestra aplicación y cuando hagáis clic os llevará a la página que hayáis configurado al efecto :).


Cómo evitar acciones de zoom en WebBrowser embebido en WP7

Seguramente muchas veces se os ha presentado la situación en la que tenéis que presentar contenido en HTML en vuestras aplicaciones, y la labor de pasar ese HTML a XAML para presentarlo en una cosa diferente a un WebBrowser embebido es algo inviable, casi. El problema viene cuando queréis tener una experiencia consistente en la aplicación, y el hecho de que WebBrowser no tenga un atributo para prohibir cualquier tipo de zoom o que usar un viewport tenga un comportamiento errático, no ayuda. Pero no os preocupéis, hay solución.

Nota: truco especialmente útil para los que osen a usar PhoneGap.

Como sabréis algunos, un WebBrowser no es un control solamente, sino que es la unión de varios controles. Específicamente:

-WebBrowser
  -Border
    -Border
      -PanZoomContainer
        -Grid
          -Border
            -ContentPresenter
              -TileHost

De hecho es bastante sencillo, un TileHost que es el núcleo de IE9, y el PanZoomContainer, donde se procesan las acciones del touch y clic. PanZoomContainer coge las acciones, las pasa al TileHost y este le da el feedback para convertirlo en gestos de pinch-to-zoom. ¿Qué significa esto? Pues básicamente significa que podemos intereptar esas acciones en el Border que hay en medio del PanZoom y el TileHost antes de que se procesen y hacer que aunque el usuario realice el gesto de pinch-to-zoom, el WebBrowser no se mueva en absoluto (no como con el viewport, que hace un amago de zoom para volver al estado original).

La solución, en código

Ya hemos dado la idea general de la solución, de qué es lo que haremos, ahora vamos al cómo.

Para empezar, abrimos un proyecto de Windows Phone, insertamos un WebBrowser, un TextBox y un botón. Supongamos que el WebBrowser se llama wb, el TextBox tb y el Botón bt. Soy insultantemente original con mis nombres. Añadimos un evento clic para el botón y escribimos dentro:


wb.Navigate(tb.Text);

Si insertamos cualquier web, el usuario sigue pudiendo hacer algo de zoom, incluso con viewport tag.
Vamos al lío, en líneas generales será esto:

  1. Añadimos LinqToVisualTree a nuestro proyecto.
  2. Añadimos WebBrowserHelper al proyecto.
  3. Instanciamos un WebBrowserHelper con el WebBrowser.
  4. ?????.
  5. PROFIT!!!

Primer paso

Añadimos LinqToVisualTree: Proyecto->agregar clase->”LinqToVisualTree” y pegamos esto:

<pre>using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

namespace LinqToVisualTree
{
    /// <summary>
    /// Adapts a DependencyObject to provide methods required for generate
    /// a Linq To Tree API
    /// </summary>
    public class VisualTreeAdapter : ILinqTree<DependencyObject>
    {
        private DependencyObject _item;

        public VisualTreeAdapter(DependencyObject item)
        {
            _item = item;
        }

        public IEnumerable<DependencyObject> Children()
        {
            int childrenCount = VisualTreeHelper.GetChildrenCount(_item);
            for (int i = 0; i < childrenCount; i++)
            {
                yield return VisualTreeHelper.GetChild(_item, i);
            }
        }

        public DependencyObject Parent
        {
            get
            {
                return VisualTreeHelper.GetParent(_item);
            }
        }
    }
}

namespace LinqToVisualTree
{
    /// <summary>
    /// Defines an interface that must be implemented to generate the LinqToTree methods
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ILinqTree<T>
    {
        IEnumerable<T> Children();

        T Parent { get; }
    }

    public static class TreeExtensions
    {
        /// <summary>
        /// Returns a collection of descendant elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Descendants(this DependencyObject item)
        {
            ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
            foreach (var child in adapter.Children())
            {
                yield return child;

                foreach (var grandChild in child.Descendants())
                {
                    yield return grandChild;
                }
            }
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf(this DependencyObject item)
        {
            yield return item;

            foreach (var child in item.Descendants())
            {
                yield return child;
            }
        }

        /// <summary>
        /// Returns a collection of ancestor elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Ancestors(this DependencyObject item)
        {
            ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);

            var parent = adapter.Parent;
            while (parent != null)
            {
                yield return parent;
                adapter = new VisualTreeAdapter(parent);
                parent = adapter.Parent;
            }
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf(this DependencyObject item)
        {
            yield return item;

            foreach (var ancestor in item.Ancestors())
            {
                yield return ancestor;
            }
        }

        /// <summary>
        /// Returns a collection of child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements(this DependencyObject item)
        {
            ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
            foreach (var child in adapter.Children())
            {
                yield return child;
            }
        }

        /// <summary>
        /// Returns a collection of the sibling elements before this node, in document order.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsBeforeSelf(this DependencyObject item)
        {
            if (item.Ancestors().FirstOrDefault() == null)
                yield break;
            foreach (var child in item.Ancestors().First().Elements())
            {
                if (child.Equals(item))
                    break;
                yield return child;
            }
        }

        /// <summary>
        /// Returns a collection of the after elements after this node, in document order.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAfterSelf(this DependencyObject item)
        {
            if (item.Ancestors().FirstOrDefault() == null)
                yield break;
            bool afterSelf = false;
            foreach (var child in item.Ancestors().First().Elements())
            {
                if (afterSelf)
                    yield return child;

                if (child.Equals(item))
                    afterSelf = true;
            }
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf(this DependencyObject item)
        {
            yield return item;

            foreach (var child in item.Elements())
            {
                yield return child;
            }
        }

        /// <summary>
        /// Returns a collection of descendant elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Descendants<T>(this DependencyObject item)
        {
            return item.Descendants().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of the sibling elements before this node, in document order
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsBeforeSelf<T>(this DependencyObject item)
        {
            return item.ElementsBeforeSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of the after elements after this node, in document order
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAfterSelf<T>(this DependencyObject item)
        {
            return item.ElementsAfterSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this DependencyObject item)
        {
            return item.DescendantsAndSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of ancestor elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Ancestors<T>(this DependencyObject item)
        {
            return item.Ancestors().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this DependencyObject item)
        {
            return item.AncestorsAndSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of child elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements<T>(this DependencyObject item)
        {
            return item.Elements().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this DependencyObject item)
        {
            return item.ElementsAndSelf().Where(i => i is T).Cast<DependencyObject>();
        }

    }

    public static class EnumerableTreeExtensions
    {
        /// <summary>
        /// Applies the given function to each of the items in the supplied
        /// IEnumerable.
        /// </summary>
        private static IEnumerable<DependencyObject> DrillDown(this IEnumerable<DependencyObject> items,
            Func<DependencyObject, IEnumerable<DependencyObject>> function)
        {
            foreach (var item in items)
            {
                foreach (var itemChild in function(item))
                {
                    yield return itemChild;
                }
            }
        }

        /// <summary>
        /// Applies the given function to each of the items in the supplied
        /// IEnumerable, which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> DrillDown<T>(this IEnumerable<DependencyObject> items,
            Func<DependencyObject, IEnumerable<DependencyObject>> function)
            where T : DependencyObject
        {
            foreach (var item in items)
            {
                foreach (var itemChild in function(item))
                {
                    if (itemChild is T)
                    {
                        yield return (T)itemChild;
                    }
                }
            }
        }

        /// <summary>
        /// Returns a collection of descendant elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Descendants(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.Descendants());
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.DescendantsAndSelf());
        }

        /// <summary>
        /// Returns a collection of ancestor elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Ancestors(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.Ancestors());
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.AncestorsAndSelf());
        }

        /// <summary>
        /// Returns a collection of child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.Elements());
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.ElementsAndSelf());
        }

        /// <summary>
        /// Returns a collection of descendant elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Descendants<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.Descendants());
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.DescendantsAndSelf());
        }

        /// <summary>
        /// Returns a collection of ancestor elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Ancestors<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.Ancestors());
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.AncestorsAndSelf());
        }

        /// <summary>
        /// Returns a collection of child elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.Elements());
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.ElementsAndSelf());
        }
    }
}

Ojo, mirad cómo estamos usando un espacio de nombres ¡diferente al de nuestro proyecto!, a partir de ahora tenéis que usar la directiva

using LinqToVisualTree;

Segundo paso

Añadimos WebBrowserHelper, lo mismo de antes y pegamos:

<pre>using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using LinqToVisualTree;
using Microsoft.Phone.Controls;

/// <summary>
/// Suppresses pinch zoom and optionally scrolling of the WebBrowser control
/// </summary>
public class WebBrowserHelper
{
  private WebBrowser _browser;

  /// <summary>
  /// Gets or sets whether to suppress the scrolling of
  /// the WebBrowser control;
  /// </summary>
  public bool ScrollDisabled { get; set; }

  public WebBrowserHelper(WebBrowser browser)
  {
    _browser = browser;
    browser.Loaded += new RoutedEventHandler(browser_Loaded);
  }

  private void browser_Loaded(object sender, RoutedEventArgs e)
  {
    var border = _browser.Descendants<Border>().Last() as Border;

    border.ManipulationDelta += Border_ManipulationDelta;
    border.ManipulationCompleted += Border_ManipulationCompleted;
  }

  private void Border_ManipulationCompleted(object sender,
                                            ManipulationCompletedEventArgs e)
  {
    // suppress zoom
    if (e.FinalVelocities.ExpansionVelocity.X != 0.0 ||
        e.FinalVelocities.ExpansionVelocity.Y != 0.0)
      e.Handled = true;
  }

  private void Border_ManipulationDelta(object sender,
                                        ManipulationDeltaEventArgs e)
  {
    // suppress zoom
    if (e.DeltaManipulation.Scale.X != 0.0 ||
        e.DeltaManipulation.Scale.Y != 0.0)
      e.Handled = true;
  }
}

Tercer paso

Esto es lo más complicado que vais a ver de todo el proceso #ironía.


WebBrowserHelper wbh = new WebBrowserHelper(wb); //wb era nuestro WebBrowser integrado en la app

wb.Navigate(tb.Text);

Y ya está todo hecho! Ahora las aplicaciones que hagáis con WebBrowser (como lectores RSS o PhoneGap) tendrán esa consistencia de uso como si hubiérais pasado todo el HTML a un TextBlock 😉


Páginas:12